12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824 |
- /*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
- /*****************************************************************************
- Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
- All rights reserved.
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are
- met:
- * Redistributions of source code must retain the above
- copyright notice, this list of conditions and the
- following disclaimer.
- * Redistributions in binary form must reproduce the
- above copyright notice, this list of conditions
- and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- * Neither the name of the University of Illinois
- nor the names of its contributors may be used to
- endorse or promote products derived from this
- software without specific prior written permission.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
- /*****************************************************************************
- written by
- Yunhong Gu, last updated 02/28/2012
- modified by
- Haivision Systems Inc.
- *****************************************************************************/
- #include "platform_sys.h"
- // Linux specific
- #ifdef SRT_ENABLE_BINDTODEVICE
- #include <linux/if.h>
- #endif
- #include <cmath>
- #include <sstream>
- #include <algorithm>
- #include <iterator>
- #include "srt.h"
- #include "access_control.h" // Required for SRT_REJX_FALLBACK
- #include "queue.h"
- #include "api.h"
- #include "core.h"
- #include "logging.h"
- #include "crypto.h"
- #include "logging_api.h" // Required due to containing extern srt_logger_config
- #include "logger_defs.h"
- #if !HAVE_CXX11
- // for pthread_once
- #include <pthread.h>
- #endif
- // Again, just in case when some "smart guy" provided such a global macro
- #ifdef min
- #undef min
- #endif
- #ifdef max
- #undef max
- #endif
- using namespace std;
- using namespace srt;
- using namespace srt::sync;
- using namespace srt_logging;
- const SRTSOCKET UDT::INVALID_SOCK = srt::CUDT::INVALID_SOCK;
- const int UDT::ERROR = srt::CUDT::ERROR;
- //#define SRT_CMD_HSREQ 1 /* SRT Handshake Request (sender) */
- #define SRT_CMD_HSREQ_MINSZ 8 /* Minumum Compatible (1.x.x) packet size (bytes) */
- #define SRT_CMD_HSREQ_SZ 12 /* Current version packet size */
- #if SRT_CMD_HSREQ_SZ > SRT_CMD_MAXSZ
- #error SRT_CMD_MAXSZ too small
- #endif
- /* Handshake Request (Network Order)
- 0[31..0]: SRT version SRT_DEF_VERSION
- 1[31..0]: Options 0 [ | SRT_OPT_TSBPDSND ][ | SRT_OPT_HAICRYPT ]
- 2[31..16]: TsbPD resv 0
- 2[15..0]: TsbPD delay [0..60000] msec
- */
- //#define SRT_CMD_HSRSP 2 /* SRT Handshake Response (receiver) */
- #define SRT_CMD_HSRSP_MINSZ 8 /* Minumum Compatible (1.x.x) packet size (bytes) */
- #define SRT_CMD_HSRSP_SZ 12 /* Current version packet size */
- #if SRT_CMD_HSRSP_SZ > SRT_CMD_MAXSZ
- #error SRT_CMD_MAXSZ too small
- #endif
- /* Handshake Response (Network Order)
- 0[31..0]: SRT version SRT_DEF_VERSION
- 1[31..0]: Options 0 [ | SRT_OPT_TSBPDRCV [| SRT_OPT_TLPKTDROP ]][ | SRT_OPT_HAICRYPT]
- [ | SRT_OPT_NAKREPORT ] [ | SRT_OPT_REXMITFLG ]
- 2[31..16]: TsbPD resv 0
- 2[15..0]: TsbPD delay [0..60000] msec
- */
- extern const SRT_SOCKOPT srt_post_opt_list [SRT_SOCKOPT_NPOST] = {
- SRTO_SNDSYN,
- SRTO_RCVSYN,
- SRTO_LINGER,
- SRTO_SNDTIMEO,
- SRTO_RCVTIMEO,
- SRTO_MAXBW,
- SRTO_INPUTBW,
- SRTO_MININPUTBW,
- SRTO_OHEADBW,
- SRTO_SNDDROPDELAY,
- SRTO_DRIFTTRACER,
- SRTO_LOSSMAXTTL
- };
- const int32_t
- SRTO_R_PREBIND = BIT(0), //< cannot be modified after srt_bind()
- SRTO_R_PRE = BIT(1), //< cannot be modified after connection is established
- SRTO_POST_SPEC = BIT(2); //< executes some action after setting the option
- namespace srt
- {
- struct SrtOptionAction
- {
- int flags[SRTO_E_SIZE];
- std::map<SRT_SOCKOPT, std::string> private_default;
- SrtOptionAction()
- {
- // Set everything to 0 to clear all flags
- // When an option isn't present here, it means that:
- // * it is not settable, or
- // * the option is POST (non-restricted)
- // * it has no post-actions
- // The post-action may be defined independently on restrictions.
- memset(flags, 0, sizeof flags);
- flags[SRTO_MSS] = SRTO_R_PREBIND;
- flags[SRTO_FC] = SRTO_R_PRE;
- flags[SRTO_SNDBUF] = SRTO_R_PREBIND;
- flags[SRTO_RCVBUF] = SRTO_R_PREBIND;
- flags[SRTO_UDP_SNDBUF] = SRTO_R_PREBIND;
- flags[SRTO_UDP_RCVBUF] = SRTO_R_PREBIND;
- flags[SRTO_RENDEZVOUS] = SRTO_R_PRE;
- flags[SRTO_REUSEADDR] = SRTO_R_PREBIND;
- flags[SRTO_MAXBW] = SRTO_POST_SPEC;
- flags[SRTO_SENDER] = SRTO_R_PRE;
- flags[SRTO_TSBPDMODE] = SRTO_R_PRE;
- flags[SRTO_LATENCY] = SRTO_R_PRE;
- flags[SRTO_INPUTBW] = SRTO_POST_SPEC;
- flags[SRTO_MININPUTBW] = SRTO_POST_SPEC;
- flags[SRTO_OHEADBW] = SRTO_POST_SPEC;
- flags[SRTO_PASSPHRASE] = SRTO_R_PRE;
- flags[SRTO_PBKEYLEN] = SRTO_R_PRE;
- flags[SRTO_IPTTL] = SRTO_R_PREBIND;
- flags[SRTO_IPTOS] = SRTO_R_PREBIND;
- flags[SRTO_TLPKTDROP] = SRTO_R_PRE;
- flags[SRTO_SNDDROPDELAY] = SRTO_POST_SPEC;
- flags[SRTO_NAKREPORT] = SRTO_R_PRE;
- flags[SRTO_VERSION] = SRTO_R_PRE;
- flags[SRTO_CONNTIMEO] = SRTO_R_PRE;
- flags[SRTO_LOSSMAXTTL] = SRTO_POST_SPEC;
- flags[SRTO_RCVLATENCY] = SRTO_R_PRE;
- flags[SRTO_PEERLATENCY] = SRTO_R_PRE;
- flags[SRTO_MINVERSION] = SRTO_R_PRE;
- flags[SRTO_STREAMID] = SRTO_R_PRE;
- flags[SRTO_CONGESTION] = SRTO_R_PRE;
- flags[SRTO_MESSAGEAPI] = SRTO_R_PRE;
- flags[SRTO_PAYLOADSIZE] = SRTO_R_PRE;
- flags[SRTO_TRANSTYPE] = SRTO_R_PREBIND;
- flags[SRTO_KMREFRESHRATE] = SRTO_R_PRE;
- flags[SRTO_KMPREANNOUNCE] = SRTO_R_PRE;
- flags[SRTO_ENFORCEDENCRYPTION] = SRTO_R_PRE;
- flags[SRTO_IPV6ONLY] = SRTO_R_PREBIND;
- flags[SRTO_PEERIDLETIMEO] = SRTO_R_PRE;
- #ifdef SRT_ENABLE_BINDTODEVICE
- flags[SRTO_BINDTODEVICE] = SRTO_R_PREBIND;
- #endif
- #if ENABLE_BONDING
- flags[SRTO_GROUPCONNECT] = SRTO_R_PRE;
- flags[SRTO_GROUPMINSTABLETIMEO]= SRTO_R_PRE;
- #endif
- flags[SRTO_PACKETFILTER] = SRTO_R_PRE;
- flags[SRTO_RETRANSMITALGO] = SRTO_R_PRE;
- #ifdef ENABLE_AEAD_API_PREVIEW
- flags[SRTO_CRYPTOMODE] = SRTO_R_PRE;
- #endif
- // For "private" options (not derived from the listener
- // socket by an accepted socket) provide below private_default
- // to which these options will be reset after blindly
- // copying the option object from the listener socket.
- // Note that this option cannot have runtime-dependent
- // default value, like options affected by SRTO_TRANSTYPE.
- // Options may be of different types, but this value should be only
- // used as a source of the value. For example, in case of int64_t you'd
- // have to place here a string of 8 characters. It should be copied
- // always in the hardware order, as this is what will be directly
- // passed to a setting function.
- private_default[SRTO_STREAMID] = string();
- }
- };
- const SrtOptionAction s_sockopt_action;
- } // namespace srt
- #if HAVE_CXX11
- CUDTUnited& srt::CUDT::uglobal()
- {
- static CUDTUnited instance;
- return instance;
- }
- #else // !HAVE_CXX11
- static pthread_once_t s_UDTUnitedOnce = PTHREAD_ONCE_INIT;
- static CUDTUnited *getInstance()
- {
- static CUDTUnited instance;
- return &instance;
- }
- CUDTUnited& srt::CUDT::uglobal()
- {
- // We don't want lock each time, pthread_once can be faster than mutex.
- pthread_once(&s_UDTUnitedOnce, reinterpret_cast<void (*)()>(getInstance));
- return *getInstance();
- }
- #endif
- void srt::CUDT::construct()
- {
- m_pSndBuffer = NULL;
- m_pRcvBuffer = NULL;
- m_pSndLossList = NULL;
- m_pRcvLossList = NULL;
- m_iReorderTolerance = 0;
- // How many times so far the packet considered lost has been received
- // before TTL expires.
- m_iConsecEarlyDelivery = 0;
- m_iConsecOrderedDelivery = 0;
- m_pSndQueue = NULL;
- m_pRcvQueue = NULL;
- m_pSNode = NULL;
- m_pRNode = NULL;
- // Will be reset to 0 for HSv5, this value is important for HSv4.
- m_iSndHsRetryCnt = SRT_MAX_HSRETRY + 1;
- m_PeerID = 0;
- m_bOpened = false;
- m_bListening = false;
- m_bConnecting = false;
- m_bConnected = false;
- m_bClosing = false;
- m_bShutdown = false;
- m_bBroken = false;
- m_bBreakAsUnstable = false;
- // TODO: m_iBrokenCounter should be still set to some default.
- m_bPeerHealth = true;
- m_RejectReason = SRT_REJ_UNKNOWN;
- m_tsLastReqTime.store(steady_clock::time_point());
- m_SrtHsSide = HSD_DRAW;
- m_uPeerSrtVersion = 0; // Not defined until connected.
- m_iTsbPdDelay_ms = 0;
- m_iPeerTsbPdDelay_ms = 0;
- m_bPeerTsbPd = false;
- m_bTsbPd = false;
- m_bTsbPdAckWakeup = false;
- m_bGroupTsbPd = false;
- m_bPeerTLPktDrop = false;
- // Initilize mutex and condition variables.
- initSynch();
- // TODO: Uncomment when the callback is implemented.
- // m_cbPacketArrival.set(this, &CUDT::defaultPacketArrival);
- }
- srt::CUDT::CUDT(CUDTSocket* parent)
- : m_parent(parent)
- #ifdef ENABLE_MAXREXMITBW
- , m_SndRexmitRate(sync::steady_clock::now())
- #endif
- , m_iISN(-1)
- , m_iPeerISN(-1)
- {
- construct();
- (void)SRT_DEF_VERSION;
- // Runtime fields
- #if ENABLE_BONDING
- m_HSGroupType = SRT_GTYPE_UNDEFINED;
- #endif
- m_bTLPktDrop = true; // Too-late Packet Drop
- m_pCache = NULL;
- // This is in order to set it ANY kind of initial value, however
- // this value should not be used when not connected and should be
- // updated in the handshake. When this value is 0, it means that
- // packets shall not be sent, as the other party doesn't have a
- // room to receive and store it. Therefore this value should be
- // overridden before any sending happens.
- m_iFlowWindowSize = 0;
- }
- srt::CUDT::CUDT(CUDTSocket* parent, const CUDT& ancestor)
- : m_parent(parent)
- #ifdef ENABLE_MAXREXMITBW
- , m_SndRexmitRate(sync::steady_clock::now())
- #endif
- , m_iISN(-1)
- , m_iPeerISN(-1)
- {
- construct();
- // XXX Consider all below fields (except m_bReuseAddr) to be put
- // into a separate class for easier copying.
- m_config = ancestor.m_config;
- // Reset values that shall not be derived to default ones.
- // These declarations should be consistent with SRTO_R_PRIVATE flag.
- for (size_t i = 0; i < Size(s_sockopt_action.flags); ++i)
- {
- const string* pdef = map_getp(s_sockopt_action.private_default, SRT_SOCKOPT(i));
- if (pdef)
- {
- try
- {
- // Ignore errors here - this is a development-time granted
- // value, not user-provided value.
- m_config.set(SRT_SOCKOPT(i), pdef->data(), (int) pdef->size());
- }
- catch (...)
- {
- LOGC(gglog.Error, log << "IPE: failed to set a declared default option!");
- }
- }
- }
- m_SrtHsSide = ancestor.m_SrtHsSide; // actually it sets it to HSD_RESPONDER
- m_bTLPktDrop = ancestor.m_bTLPktDrop;
- m_iReorderTolerance = m_config.iMaxReorderTolerance; // Initialize with maximum value
- // Runtime
- m_pCache = ancestor.m_pCache;
- }
- srt::CUDT::~CUDT()
- {
- // release mutex/condtion variables
- destroySynch();
- // destroy the data structures
- delete m_pSndBuffer;
- delete m_pRcvBuffer;
- delete m_pSndLossList;
- delete m_pRcvLossList;
- delete m_pSNode;
- delete m_pRNode;
- }
- void srt::CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen)
- {
- if (m_bBroken || m_bClosing)
- throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
- // Match check (confirm optName as index for s_sockopt_action)
- if (int(optName) < 0 || int(optName) >= int(SRTO_E_SIZE))
- throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
- // Restriction check
- const int oflags = s_sockopt_action.flags[optName];
- ScopedLock cg (m_ConnectionLock);
- ScopedLock sendguard (m_SendLock);
- ScopedLock recvguard (m_RecvLock);
- HLOGC(aclog.Debug,
- log << CONID() << "OPTION: #" << optName << " value:" << FormatBinaryString((uint8_t*)optval, optlen));
- if (IsSet(oflags, SRTO_R_PREBIND) && m_bOpened)
- throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0);
- if (IsSet(oflags, SRTO_R_PRE) && (m_bConnected || m_bConnecting || m_bListening))
- throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
- // Option execution. If this returns -1, there's no such option.
- const int status = m_config.set(optName, optval, optlen);
- if (status == -1)
- {
- LOGC(aclog.Error, log << CONID() << "OPTION: #" << optName << " UNKNOWN");
- throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
- }
- // Post-action, if applicable
- if (IsSet(oflags, SRTO_POST_SPEC) && m_bConnected)
- {
- switch (optName)
- {
- case SRTO_MAXBW:
- updateCC(TEV_INIT, EventVariant(TEV_INIT_RESET));
- break;
- case SRTO_INPUTBW:
- case SRTO_MININPUTBW:
- updateCC(TEV_INIT, EventVariant(TEV_INIT_INPUTBW));
- break;
- case SRTO_OHEADBW:
- updateCC(TEV_INIT, EventVariant(TEV_INIT_OHEADBW));
- break;
- case SRTO_LOSSMAXTTL:
- m_iReorderTolerance = m_config.iMaxReorderTolerance;
- default: break;
- }
- }
- }
- void srt::CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen)
- {
- ScopedLock cg(m_ConnectionLock);
- switch (optName)
- {
- case SRTO_MSS:
- *(int *)optval = m_config.iMSS;
- optlen = sizeof(int);
- break;
- case SRTO_SNDSYN:
- *(bool *)optval = m_config.bSynSending;
- optlen = sizeof(bool);
- break;
- case SRTO_RCVSYN:
- *(bool *)optval = m_config.bSynRecving;
- optlen = sizeof(bool);
- break;
- case SRTO_ISN:
- *(int *)optval = m_iISN;
- optlen = sizeof(int);
- break;
- case SRTO_FC:
- *(int *)optval = m_config.iFlightFlagSize;
- optlen = sizeof(int);
- break;
- case SRTO_SNDBUF:
- *(int *)optval = m_config.iSndBufSize * (m_config.iMSS - CPacket::UDP_HDR_SIZE);
- optlen = sizeof(int);
- break;
- case SRTO_RCVBUF:
- *(int *)optval = m_config.iRcvBufSize * (m_config.iMSS - CPacket::UDP_HDR_SIZE);
- optlen = sizeof(int);
- break;
- case SRTO_LINGER:
- if (optlen < (int)(sizeof(linger)))
- throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
- *(linger *)optval = m_config.Linger;
- optlen = sizeof(linger);
- break;
- case SRTO_UDP_SNDBUF:
- *(int *)optval = m_config.iUDPSndBufSize;
- optlen = sizeof(int);
- break;
- case SRTO_UDP_RCVBUF:
- *(int *)optval = m_config.iUDPRcvBufSize;
- optlen = sizeof(int);
- break;
- case SRTO_RENDEZVOUS:
- *(bool *)optval = m_config.bRendezvous;
- optlen = sizeof(bool);
- break;
- case SRTO_SNDTIMEO:
- *(int *)optval = m_config.iSndTimeOut;
- optlen = sizeof(int);
- break;
- case SRTO_RCVTIMEO:
- *(int *)optval = m_config.iRcvTimeOut;
- optlen = sizeof(int);
- break;
- case SRTO_REUSEADDR:
- *(bool *)optval = m_config.bReuseAddr;
- optlen = sizeof(bool);
- break;
- case SRTO_MAXBW:
- if (size_t(optlen) < sizeof(m_config.llMaxBW))
- throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
- *(int64_t *)optval = m_config.llMaxBW;
- optlen = sizeof(int64_t);
- break;
- case SRTO_INPUTBW:
- if (size_t(optlen) < sizeof(m_config.llInputBW))
- throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
- *(int64_t*)optval = m_config.llInputBW;
- optlen = sizeof(int64_t);
- break;
- case SRTO_MININPUTBW:
- if (size_t(optlen) < sizeof (m_config.llMinInputBW))
- throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
- *(int64_t*)optval = m_config.llMinInputBW;
- optlen = sizeof(int64_t);
- break;
- case SRTO_OHEADBW:
- *(int32_t *)optval = m_config.iOverheadBW;
- optlen = sizeof(int32_t);
- break;
- case SRTO_STATE:
- *(int32_t *)optval = uglobal().getStatus(m_SocketID);
- optlen = sizeof(int32_t);
- break;
- case SRTO_EVENT:
- {
- int32_t event = 0;
- if (m_bBroken)
- event |= SRT_EPOLL_ERR;
- else
- {
- enterCS(m_RecvLock);
- if (m_pRcvBuffer && isRcvBufferReady())
- event |= SRT_EPOLL_IN;
- leaveCS(m_RecvLock);
- if (m_pSndBuffer && (m_config.iSndBufSize > m_pSndBuffer->getCurrBufSize()))
- event |= SRT_EPOLL_OUT;
- }
- *(int32_t *)optval = event;
- optlen = sizeof(int32_t);
- break;
- }
- case SRTO_SNDDATA:
- if (m_pSndBuffer)
- *(int32_t *)optval = m_pSndBuffer->getCurrBufSize();
- else
- *(int32_t *)optval = 0;
- optlen = sizeof(int32_t);
- break;
- case SRTO_RCVDATA:
- if (m_pRcvBuffer)
- {
- enterCS(m_RecvLock);
- *(int32_t *)optval = m_pRcvBuffer->getRcvDataSize();
- leaveCS(m_RecvLock);
- }
- else
- *(int32_t *)optval = 0;
- optlen = sizeof(int32_t);
- break;
- case SRTO_IPTTL:
- if (m_bOpened)
- *(int32_t *)optval = m_pSndQueue->getIpTTL();
- else
- *(int32_t *)optval = m_config.iIpTTL;
- optlen = sizeof(int32_t);
- break;
- case SRTO_IPTOS:
- if (m_bOpened)
- *(int32_t *)optval = m_pSndQueue->getIpToS();
- else
- *(int32_t *)optval = m_config.iIpToS;
- optlen = sizeof(int32_t);
- break;
- case SRTO_BINDTODEVICE:
- #ifdef SRT_ENABLE_BINDTODEVICE
- if (optlen < IFNAMSIZ)
- throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
- if (m_bOpened && m_pSndQueue->getBind(((char*)optval), optlen))
- {
- optlen = strlen((char*)optval);
- break;
- }
- // Fallback: return from internal data
- optlen = (int)m_config.sBindToDevice.copy((char*)optval, (size_t)optlen - 1);
- ((char*)optval)[optlen] = '\0';
- #else
- LOGC(smlog.Error, log << "SRTO_BINDTODEVICE is not supported on that platform");
- throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
- #endif
- break;
- case SRTO_SENDER:
- *(bool *)optval = m_config.bDataSender;
- optlen = sizeof(bool);
- break;
- case SRTO_TSBPDMODE:
- *(bool *)optval = m_config.bTSBPD;
- optlen = sizeof(bool);
- break;
- case SRTO_LATENCY:
- case SRTO_RCVLATENCY:
- if (m_bConnected)
- *(int32_t *)optval = m_iTsbPdDelay_ms;
- else
- *(int32_t *)optval = m_config.iRcvLatency;
- optlen = sizeof(int32_t);
- break;
- case SRTO_PEERLATENCY:
- if (m_bConnected)
- *(int32_t *)optval = m_iPeerTsbPdDelay_ms;
- else
- *(int32_t *)optval = m_config.iPeerLatency;
- optlen = sizeof(int32_t);
- break;
- case SRTO_TLPKTDROP:
- if (m_bConnected)
- *(bool *)optval = m_bTLPktDrop;
- else
- *(bool *)optval = m_config.bTLPktDrop;
- optlen = sizeof(bool);
- break;
- case SRTO_SNDDROPDELAY:
- *(int32_t *)optval = m_config.iSndDropDelay;
- optlen = sizeof(int32_t);
- break;
- case SRTO_PBKEYLEN:
- if (m_pCryptoControl)
- *(int32_t *)optval = (int32_t) m_pCryptoControl->KeyLen(); // Running Key length.
- else
- *(int32_t *)optval = m_config.iSndCryptoKeyLen; // May be 0.
- optlen = sizeof(int32_t);
- break;
- case SRTO_KMSTATE:
- if (!m_pCryptoControl)
- *(int32_t *)optval = SRT_KM_S_UNSECURED;
- else if (m_config.bDataSender)
- *(int32_t *)optval = m_pCryptoControl->m_SndKmState;
- else
- *(int32_t *)optval = m_pCryptoControl->m_RcvKmState;
- optlen = sizeof(int32_t);
- break;
- case SRTO_SNDKMSTATE: // State imposed by Agent depending on PW and KMX
- if (m_pCryptoControl)
- *(int32_t *)optval = m_pCryptoControl->m_SndKmState;
- else
- *(int32_t *)optval = SRT_KM_S_UNSECURED;
- optlen = sizeof(int32_t);
- break;
- case SRTO_RCVKMSTATE: // State returned by Peer as informed during KMX
- if (m_pCryptoControl)
- *(int32_t *)optval = m_pCryptoControl->m_RcvKmState;
- else
- *(int32_t *)optval = SRT_KM_S_UNSECURED;
- optlen = sizeof(int32_t);
- break;
- case SRTO_LOSSMAXTTL:
- *(int32_t*)optval = m_config.iMaxReorderTolerance;
- optlen = sizeof(int32_t);
- break;
- case SRTO_NAKREPORT:
- *(bool *)optval = m_config.bRcvNakReport;
- optlen = sizeof(bool);
- break;
- case SRTO_VERSION:
- *(int32_t *)optval = m_config.uSrtVersion;
- optlen = sizeof(int32_t);
- break;
- case SRTO_PEERVERSION:
- *(int32_t *)optval = m_uPeerSrtVersion;
- optlen = sizeof(int32_t);
- break;
- case SRTO_CONNTIMEO:
- *(int*)optval = (int) count_milliseconds(m_config.tdConnTimeOut);
- optlen = sizeof(int);
- break;
- case SRTO_DRIFTTRACER:
- *(bool*)optval = m_config.bDriftTracer;
- optlen = sizeof(bool);
- break;
- case SRTO_MINVERSION:
- *(uint32_t *)optval = m_config.uMinimumPeerSrtVersion;
- optlen = sizeof(uint32_t);
- break;
- case SRTO_STREAMID:
- if (size_t(optlen) < m_config.sStreamName.size() + 1)
- throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
- optlen = (int)m_config.sStreamName.copy((char*)optval, (size_t)optlen - 1);
- ((char*)optval)[optlen] = '\0';
- break;
- case SRTO_CONGESTION:
- if (size_t(optlen) < m_config.sCongestion.size() + 1)
- throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
- optlen = (int)m_config.sCongestion.copy((char*)optval, (size_t)optlen - 1);
- ((char*)optval)[optlen] = '\0';
- break;
- case SRTO_MESSAGEAPI:
- optlen = sizeof(bool);
- *(bool *)optval = m_config.bMessageAPI;
- break;
- case SRTO_PAYLOADSIZE:
- optlen = sizeof(int);
- *(int *)optval = (int) m_config.zExpPayloadSize;
- break;
- case SRTO_KMREFRESHRATE:
- optlen = sizeof(int);
- *(int*)optval = (int)m_config.uKmRefreshRatePkt;
- break;
- case SRTO_KMPREANNOUNCE:
- optlen = sizeof(int);
- *(int*)optval = (int)m_config.uKmPreAnnouncePkt;
- break;
- #if ENABLE_BONDING
- case SRTO_GROUPCONNECT:
- optlen = sizeof (int);
- *(int*)optval = m_config.iGroupConnect;
- break;
- case SRTO_GROUPMINSTABLETIMEO:
- optlen = sizeof(int);
- *(int*)optval = (int)m_config.uMinStabilityTimeout_ms;
- break;
- case SRTO_GROUPTYPE:
- optlen = sizeof (int);
- *(int*)optval = m_HSGroupType;
- break;
- #endif
- case SRTO_ENFORCEDENCRYPTION:
- optlen = sizeof(bool);
- *(bool *)optval = m_config.bEnforcedEnc;
- break;
- case SRTO_IPV6ONLY:
- optlen = sizeof(int);
- *(int *)optval = m_config.iIpV6Only;
- break;
- case SRTO_PEERIDLETIMEO:
- *(int *)optval = m_config.iPeerIdleTimeout_ms;
- optlen = sizeof(int);
- break;
- case SRTO_PACKETFILTER:
- if (size_t(optlen) < m_config.sPacketFilterConfig.size() + 1)
- throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
- optlen = (int)m_config.sPacketFilterConfig.copy((char*)optval, (size_t)optlen - 1);
- ((char*)optval)[optlen] = '\0';
- break;
- case SRTO_RETRANSMITALGO:
- *(int32_t *)optval = m_config.iRetransmitAlgo;
- optlen = sizeof(int32_t);
- break;
- #ifdef ENABLE_AEAD_API_PREVIEW
- case SRTO_CRYPTOMODE:
- if (m_pCryptoControl)
- *(int32_t*)optval = m_pCryptoControl->getCryptoMode();
- else
- *(int32_t*)optval = m_config.iCryptoMode;
- optlen = sizeof(int32_t);
- break;
- #endif
- default:
- throw CUDTException(MJ_NOTSUP, MN_NONE, 0);
- }
- }
- #if ENABLE_BONDING
- SRT_ERRNO srt::CUDT::applyMemberConfigObject(const SRT_SocketOptionObject& opt)
- {
- SRT_SOCKOPT this_opt = SRTO_VERSION;
- for (size_t i = 0; i < opt.options.size(); ++i)
- {
- SRT_SocketOptionObject::SingleOption* o = opt.options[i];
- HLOGC(smlog.Debug, log << CONID() << "applyMemberConfigObject: OPTION @" << m_SocketID << " #" << o->option);
- this_opt = SRT_SOCKOPT(o->option);
- setOpt(this_opt, o->storage, o->length);
- }
- return SRT_SUCCESS;
- }
- #endif
- bool srt::CUDT::setstreamid(SRTSOCKET u, const std::string &sid)
- {
- CUDT *that = getUDTHandle(u);
- if (!that)
- return false;
- if (sid.size() > CSrtConfig::MAX_SID_LENGTH)
- return false;
- if (that->m_bConnected)
- return false;
- that->m_config.sStreamName.set(sid);
- return true;
- }
- string srt::CUDT::getstreamid(SRTSOCKET u)
- {
- CUDT *that = getUDTHandle(u);
- if (!that)
- return "";
- return that->m_config.sStreamName.str();
- }
- // XXX REFACTOR: Make common code for CUDT constructor and clearData,
- // possibly using CUDT::construct.
- // Initial sequence number, loss, acknowledgement, etc.
- void srt::CUDT::clearData()
- {
- m_iMaxSRTPayloadSize = m_config.iMSS - CPacket::UDP_HDR_SIZE - CPacket::HDR_SIZE;
- HLOGC(cnlog.Debug, log << CONID() << "clearData: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize);
- m_iEXPCount = 1;
- m_iBandwidth = 1; // pkts/sec
- // XXX use some constant for this 16
- m_iDeliveryRate = 16;
- m_iByteDeliveryRate = 16 * m_iMaxSRTPayloadSize;
- m_iAckSeqNo = 0;
- m_tsLastAckTime = steady_clock::now();
- // trace information
- {
- ScopedLock stat_lock(m_StatsLock);
- m_stats.tsStartTime = steady_clock::now();
- m_stats.sndr.reset();
- m_stats.rcvr.reset();
- m_stats.tsLastSampleTime = steady_clock::now();
- m_stats.traceReorderDistance = 0;
- m_stats.sndDuration = m_stats.m_sndDurationTotal = 0;
- }
- // Resetting these data because this happens when agent isn't connected.
- m_bPeerTsbPd = false;
- m_iPeerTsbPdDelay_ms = 0;
- // TSBPD as state should be set to FALSE here.
- // Only when the HSREQ handshake is exchanged,
- // should they be set to possibly true.
- m_bTsbPd = false;
- m_bGroupTsbPd = false;
- m_iTsbPdDelay_ms = m_config.iRcvLatency;
- m_bTLPktDrop = m_config.bTLPktDrop;
- m_bPeerTLPktDrop = false;
- m_bPeerNakReport = false;
- m_bPeerRexmitFlag = false;
- m_RdvState = CHandShake::RDV_INVALID;
- m_tsRcvPeerStartTime = steady_clock::time_point();
- }
- void srt::CUDT::open()
- {
- ScopedLock cg(m_ConnectionLock);
- clearData();
- // structures for queue
- if (m_pSNode == NULL)
- m_pSNode = new CSNode;
- m_pSNode->m_pUDT = this;
- m_pSNode->m_tsTimeStamp = steady_clock::now();
- m_pSNode->m_iHeapLoc = -1;
- if (m_pRNode == NULL)
- m_pRNode = new CRNode;
- m_pRNode->m_pUDT = this;
- m_pRNode->m_tsTimeStamp = steady_clock::now();
- m_pRNode->m_pPrev = m_pRNode->m_pNext = NULL;
- m_pRNode->m_bOnList = false;
- // Set initial values of smoothed RTT and RTT variance.
- m_iSRTT = INITIAL_RTT;
- m_iRTTVar = INITIAL_RTTVAR;
- m_bIsFirstRTTReceived = false;
- // set minimum NAK and EXP timeout to 300ms
- m_tdMinNakInterval = milliseconds_from(300);
- m_tdMinExpInterval = milliseconds_from(300);
- m_tdACKInterval = microseconds_from(COMM_SYN_INTERVAL_US);
- m_tdNAKInterval = m_tdMinNakInterval;
- const steady_clock::time_point currtime = steady_clock::now();
- m_tsLastRspTime.store(currtime);
- m_tsNextACKTime.store(currtime + m_tdACKInterval);
- m_tsNextNAKTime.store(currtime + m_tdNAKInterval);
- m_tsLastRspAckTime = currtime;
- m_tsLastSndTime.store(currtime);
- #if ENABLE_BONDING
- m_tsUnstableSince = steady_clock::time_point();
- m_tsFreshActivation = steady_clock::time_point();
- m_tsWarySince = steady_clock::time_point();
- #endif
- m_iReXmitCount = 1;
- m_iPktCount = 0;
- m_iLightACKCount = 1;
- m_tsNextSendTime = steady_clock::time_point();
- m_tdSendTimeDiff = microseconds_from(0);
- // Now UDT is opened.
- m_bOpened = true;
- }
- void srt::CUDT::setListenState()
- {
- ScopedLock cg(m_ConnectionLock);
- if (!m_bOpened)
- throw CUDTException(MJ_NOTSUP, MN_NONE, 0);
- if (m_bConnecting || m_bConnected)
- throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
- // listen can be called more than once
- if (m_bListening)
- return;
- // if there is already another socket listening on the same port
- if (m_pRcvQueue->setListener(this) < 0)
- throw CUDTException(MJ_NOTSUP, MN_BUSY, 0);
- m_bListening = true;
- }
- size_t srt::CUDT::fillSrtHandshake(uint32_t *aw_srtdata, size_t srtlen, int msgtype, int hs_version)
- {
- if (srtlen < SRT_HS_E_SIZE)
- {
- LOGC(cnlog.Fatal,
- log << CONID() << "IPE: fillSrtHandshake: buffer too small: " << srtlen << " (expected: " << SRT_HS_E_SIZE << ")");
- return 0;
- }
- srtlen = SRT_HS_E_SIZE; // We use only that much space.
- memset((aw_srtdata), 0, sizeof(uint32_t) * srtlen);
- /* Current version (1.x.x) SRT handshake */
- aw_srtdata[SRT_HS_VERSION] = m_config.uSrtVersion; /* Required version */
- aw_srtdata[SRT_HS_FLAGS] |= SrtVersionCapabilities();
- switch (msgtype)
- {
- case SRT_CMD_HSREQ:
- return fillSrtHandshake_HSREQ((aw_srtdata), srtlen, hs_version);
- case SRT_CMD_HSRSP:
- return fillSrtHandshake_HSRSP((aw_srtdata), srtlen, hs_version);
- default:
- LOGC(cnlog.Fatal, log << CONID() << "IPE: fillSrtHandshake/sendSrtMsg called with value " << msgtype);
- return 0;
- }
- }
- size_t srt::CUDT::fillSrtHandshake_HSREQ(uint32_t *aw_srtdata, size_t /* srtlen - unused */, int hs_version)
- {
- // INITIATOR sends HSREQ.
- // The TSBPD(SND|RCV) options are being set only if the TSBPD is set in the current agent.
- // The agent has a decisive power only in the range of RECEIVING the data, however it can
- // also influence the peer's latency. If agent doesn't set TSBPD mode, it doesn't send any
- // latency flags, although the peer might still want to do Rx with TSBPD. When agent sets
- // TsbPd mode, it defines latency values for Rx (itself) and Tx (peer's Rx). If peer does
- // not set TsbPd mode, it will simply ignore the proposed latency (PeerTsbPdDelay), although
- // if it has received the Rx latency as well, it must honor it and respond accordingly
- // (the latter is only in case of HSv5 and bidirectional connection).
- if (m_config.bTSBPD)
- {
- m_iTsbPdDelay_ms = m_config.iRcvLatency;
- m_iPeerTsbPdDelay_ms = m_config.iPeerLatency;
- /*
- * Sent data is real-time, use Time-based Packet Delivery,
- * set option bit and configured delay
- */
- aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDSND;
- if (hs_version < CUDT::HS_VERSION_SRT1)
- {
- // HSv4 - this uses only one value.
- aw_srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_LEG::wrap(m_iPeerTsbPdDelay_ms);
- }
- else
- {
- // HSv5 - this will be understood only since this version when this exists.
- aw_srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_SND::wrap(m_iPeerTsbPdDelay_ms);
- // And in the reverse direction.
- aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDRCV;
- aw_srtdata[SRT_HS_LATENCY] |= SRT_HS_LATENCY_RCV::wrap(m_iTsbPdDelay_ms);
- // This wasn't there for HSv4, this setting is only for the receiver.
- // HSv5 is bidirectional, so every party is a receiver.
- if (m_bTLPktDrop)
- aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_TLPKTDROP;
- }
- }
- if (m_config.bRcvNakReport)
- aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_NAKREPORT;
- // I support SRT_OPT_REXMITFLG. Do you?
- aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_REXMITFLG;
- // Declare the API used. The flag is set for "stream" API because
- // the older versions will never set this flag, but all old SRT versions use message API.
- if (!m_config.bMessageAPI)
- aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_STREAM;
- HLOGC(cnlog.Debug,
- log << CONID() << "HSREQ/snd: LATENCY[SND:" << SRT_HS_LATENCY_SND::unwrap(aw_srtdata[SRT_HS_LATENCY])
- << " RCV:" << SRT_HS_LATENCY_RCV::unwrap(aw_srtdata[SRT_HS_LATENCY]) << "] FLAGS["
- << SrtFlagString(aw_srtdata[SRT_HS_FLAGS]) << "]");
- return 3;
- }
- size_t srt::CUDT::fillSrtHandshake_HSRSP(uint32_t *aw_srtdata, size_t /* srtlen - unused */, int hs_version)
- {
- // Setting m_tsRcvPeerStartTime is done in processSrtMsg_HSREQ(), so
- // this condition will be skipped only if this function is called without
- // getting first received HSREQ. Doesn't look possible in both HSv4 and HSv5.
- if (is_zero(m_tsRcvPeerStartTime))
- {
- LOGC(cnlog.Fatal, log << CONID() << "IPE: fillSrtHandshake_HSRSP: m_tsRcvPeerStartTime NOT SET!");
- return 0;
- }
- // If Agent doesn't set TSBPD, it will not set the TSBPD flag back to the Peer.
- // The peer doesn't have be disturbed by it anyway.
- if (isOPT_TsbPd())
- {
- /*
- * We got and transposed peer start time (HandShake request timestamp),
- * we can support Timestamp-based Packet Delivery
- */
- aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDRCV;
- if (hs_version < HS_VERSION_SRT1)
- {
- // HSv4 - this uses only one value
- aw_srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_LEG::wrap(m_iTsbPdDelay_ms);
- }
- else
- {
- // HSv5 - this puts "agent's" latency into RCV field and "peer's" -
- // into SND field.
- aw_srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_RCV::wrap(m_iTsbPdDelay_ms);
- }
- }
- else
- {
- HLOGC(cnlog.Debug, log << CONID() << "HSRSP/snd: TSBPD off, NOT responding TSBPDRCV flag.");
- }
- // Hsv5, only when peer has declared TSBPD mode.
- // The flag was already set, and the value already "maximized" in processSrtMsg_HSREQ().
- if (m_bPeerTsbPd && hs_version >= HS_VERSION_SRT1)
- {
- // HSv5 is bidirectional - so send the TSBPDSND flag, and place also the
- // peer's latency into SND field.
- aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDSND;
- aw_srtdata[SRT_HS_LATENCY] |= SRT_HS_LATENCY_SND::wrap(m_iPeerTsbPdDelay_ms);
- HLOGC(cnlog.Debug,
- log << CONID()
- << "HSRSP/snd: HSv5 peer uses TSBPD, responding TSBPDSND latency=" << m_iPeerTsbPdDelay_ms);
- }
- else
- {
- HLOGC(cnlog.Debug,
- log << CONID() << "HSRSP/snd: HSv" << (hs_version == CUDT::HS_VERSION_UDT4 ? 4 : 5)
- << " with peer TSBPD=" << (m_bPeerTsbPd ? "on" : "off") << " - NOT responding TSBPDSND");
- }
- if (m_bTLPktDrop)
- aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_TLPKTDROP;
- if (m_config.bRcvNakReport)
- {
- // HSv5: Note that this setting is independent on the value of
- // m_bPeerNakReport, which represent this setting in the peer.
- aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_NAKREPORT;
- /*
- * NAK Report is so efficient at controlling bandwidth that sender TLPktDrop
- * is not needed. SRT 1.0.5 to 1.0.7 sender TLPktDrop combined with SRT 1.0
- * Timestamp-Based Packet Delivery was not well implemented and could drop
- * big I-Frame tail before sending once on low latency setups.
- * Disabling TLPktDrop in the receiver SRT Handshake Reply prevents the sender
- * from enabling Too-Late Packet Drop.
- */
- if (m_uPeerSrtVersion <= SrtVersion(1, 0, 7))
- aw_srtdata[SRT_HS_FLAGS] &= ~SRT_OPT_TLPKTDROP;
- }
- if (m_config.uSrtVersion >= SrtVersion(1, 2, 0))
- {
- if (!m_bPeerRexmitFlag)
- {
- // Peer does not request to use rexmit flag, if so,
- // we won't use as well.
- HLOGC(cnlog.Debug,
- log << CONID() << "HSRSP/snd: AGENT understands REXMIT flag, but PEER DOES NOT. NOT setting.");
- }
- else
- {
- // Request that the rexmit bit be used as a part of msgno.
- aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_REXMITFLG;
- HLOGP(cnlog.Debug, "HSRSP/snd: AGENT UNDERSTANDS REXMIT flag and PEER reported that it does, too.");
- }
- }
- else
- {
- // Since this is now in the code, it can occur only in case when you change the
- // version specification in the build configuration.
- HLOGP(cnlog.Debug, "HSRSP/snd: AGENT DOES NOT UNDERSTAND REXMIT flag");
- }
- HLOGC(cnlog.Debug,
- log << CONID() << "HSRSP/snd: LATENCY[SND:" << SRT_HS_LATENCY_SND::unwrap(aw_srtdata[SRT_HS_LATENCY])
- << " RCV:" << SRT_HS_LATENCY_RCV::unwrap(aw_srtdata[SRT_HS_LATENCY]) << "] FLAGS["
- << SrtFlagString(aw_srtdata[SRT_HS_FLAGS]) << "]");
- return 3;
- }
- size_t srt::CUDT::prepareSrtHsMsg(int cmd, uint32_t *srtdata, size_t size)
- {
- size_t srtlen = fillSrtHandshake(srtdata, size, cmd, handshakeVersion());
- HLOGC(cnlog.Debug, log << "CMD:" << MessageTypeStr(UMSG_EXT, cmd) << "(" << cmd << ") Len:"
- << int(srtlen * sizeof(int32_t))
- << " Version: " << SrtVersionString(srtdata[SRT_HS_VERSION])
- << " Flags: " << srtdata[SRT_HS_FLAGS]
- << " (" << SrtFlagString(srtdata[SRT_HS_FLAGS]) << ") sdelay:"
- << srtdata[SRT_HS_LATENCY]);
- return srtlen;
- }
- void srt::CUDT::sendSrtMsg(int cmd, uint32_t *srtdata_in, size_t srtlen_in)
- {
- CPacket srtpkt;
- int32_t srtcmd = (int32_t)cmd;
- SRT_STATIC_ASSERT(SRTDATA_MAXSIZE >= SRT_HS_E_SIZE, "SRT_CMD_MAXSZ is too small to hold all the data");
- // This will be effectively larger than SRT_HS_E_SIZE, but it will be also used for incoming data.
- uint32_t srtdata[SRTDATA_MAXSIZE];
- size_t srtlen = 0;
- if (cmd == SRT_CMD_REJECT)
- {
- // This is a value returned by processSrtMsg underlying layer, potentially
- // to be reported here. Should this happen, just send a rejection message.
- cmd = SRT_CMD_HSRSP;
- srtdata[SRT_HS_VERSION] = 0;
- }
- switch (cmd)
- {
- case SRT_CMD_HSREQ:
- case SRT_CMD_HSRSP:
- srtlen = prepareSrtHsMsg(cmd, srtdata, SRTDATA_MAXSIZE);
- break;
- case SRT_CMD_KMREQ: // Sender
- case SRT_CMD_KMRSP: // Receiver
- srtlen = srtlen_in;
- /* Msg already in network order
- * But CChannel:sendto will swap again (assuming 32-bit fields)
- * Pre-swap to cancel it.
- */
- HtoNLA(srtdata, srtdata_in, srtlen);
- m_pCryptoControl->updateKmState(cmd, srtlen); // <-- THIS function can't be moved to CUDT
- break;
- default:
- LOGC(cnlog.Error, log << "sndSrtMsg: IPE: cmd=" << cmd << " unsupported");
- break;
- }
- if (srtlen > 0)
- {
- /* srtpkt.pack will set message data in network order */
- srtpkt.pack(UMSG_EXT, &srtcmd, srtdata, srtlen * sizeof(int32_t));
- addressAndSend(srtpkt);
- }
- }
- size_t srt::CUDT::fillHsExtConfigString(uint32_t* pcmdspec, int cmd, const string& str)
- {
- uint32_t* space = pcmdspec + 1;
- size_t wordsize = (str.size() + 3) / 4;
- size_t aligned_bytesize = wordsize * 4;
- memset((space), 0, aligned_bytesize);
- memcpy((space), str.data(), str.size());
- // Preswap to little endian (in place due to possible padding zeros)
- HtoILA((space), space, wordsize);
- *pcmdspec = HS_CMDSPEC_CMD::wrap(cmd) | HS_CMDSPEC_SIZE::wrap((uint32_t) wordsize);
- return wordsize;
- }
- #if ENABLE_BONDING
- // [[using locked(m_parent->m_ControlLock)]]
- // [[using locked(s_UDTUnited.m_GlobControlLock)]]
- size_t srt::CUDT::fillHsExtGroup(uint32_t* pcmdspec)
- {
- SRT_ASSERT(m_parent->m_GroupOf != NULL);
- uint32_t* space = pcmdspec + 1;
- SRTSOCKET id = m_parent->m_GroupOf->id();
- SRT_GROUP_TYPE tp = m_parent->m_GroupOf->type();
- uint32_t flags = 0;
- // NOTE: this code remains as is for historical reasons.
- // The initial implementation stated that the peer id be
- // extracted so that it can be reported and possibly the
- // start time somehow encoded and written into the group
- // extension, but it was later seen not necessary. Therefore
- // this code remains, but now it's informational only.
- #if ENABLE_HEAVY_LOGGING
- m_parent->m_GroupOf->debugMasterData(m_SocketID);
- #endif
- // See CUDT::interpretGroup()
- uint32_t dataword = 0
- | SrtHSRequest::HS_GROUP_TYPE::wrap(tp)
- | SrtHSRequest::HS_GROUP_FLAGS::wrap(flags)
- | SrtHSRequest::HS_GROUP_WEIGHT::wrap(m_parent->m_GroupMemberData->weight);
- const uint32_t storedata [GRPD_E_SIZE] = { uint32_t(id), dataword };
- memcpy((space), storedata, sizeof storedata);
- const size_t ra_size = Size(storedata);
- *pcmdspec = HS_CMDSPEC_CMD::wrap(SRT_CMD_GROUP) | HS_CMDSPEC_SIZE::wrap(ra_size);
- return ra_size;
- }
- #endif
- size_t srt::CUDT::fillHsExtKMREQ(uint32_t* pcmdspec, size_t ki)
- {
- uint32_t* space = pcmdspec + 1;
- size_t msglen = m_pCryptoControl->getKmMsg_size(ki);
- // Make ra_size back in element unit
- // Add one extra word if the size isn't aligned to 32-bit.
- size_t ra_size = (msglen / sizeof(uint32_t)) + (msglen % sizeof(uint32_t) ? 1 : 0);
- // Store the CMD + SIZE in the next field
- *pcmdspec = HS_CMDSPEC_CMD::wrap(SRT_CMD_KMREQ) | HS_CMDSPEC_SIZE::wrap((uint32_t) ra_size);
- // Copy the key - do the endian inversion because another endian inversion
- // will be done for every control message before sending, and this KM message
- // is ALREADY in network order.
- const uint32_t* keydata = reinterpret_cast<const uint32_t*>(m_pCryptoControl->getKmMsg_data(ki));
- HLOGC(cnlog.Debug,
- log << CONID() << "createSrtHandshake: KMREQ: adding key #" << ki << " length=" << ra_size
- << " words (KmMsg_size=" << msglen << ")");
- // XXX INSECURE ": [" << FormatBinaryString((uint8_t*)keydata, msglen) << "]";
- // Yes, I know HtoNLA and NtoHLA do exactly the same operation, but I want
- // to be clear about the true intention.
- NtoHLA((space), keydata, ra_size);
- return ra_size;
- }
- size_t srt::CUDT::fillHsExtKMRSP(uint32_t* pcmdspec, const uint32_t* kmdata, size_t kmdata_wordsize)
- {
- uint32_t* space = pcmdspec + 1;
- const uint32_t failure_kmrsp[] = {SRT_KM_S_UNSECURED};
- const uint32_t* keydata = 0;
- // Shift the starting point with the value of previously added block,
- // to start with the new one.
- size_t ra_size;
- if (kmdata_wordsize == 0)
- {
- LOGC(cnlog.Warn,
- log << CONID()
- << "createSrtHandshake: Agent has PW, but Peer sent no KMREQ. Sending error KMRSP response");
- ra_size = 1;
- keydata = failure_kmrsp;
- // Update the KM state as well
- m_pCryptoControl->m_SndKmState = SRT_KM_S_NOSECRET; // Agent has PW, but Peer won't decrypt
- m_pCryptoControl->m_RcvKmState = SRT_KM_S_UNSECURED; // Peer won't encrypt as well.
- }
- else
- {
- if (!kmdata)
- {
- m_RejectReason = SRT_REJ_IPE;
- LOGC(cnlog.Fatal, log << CONID() << "createSrtHandshake: IPE: srtkm_cmd=SRT_CMD_KMRSP and no kmdata!");
- return 0;
- }
- ra_size = kmdata_wordsize;
- keydata = reinterpret_cast<const uint32_t *>(kmdata);
- }
- *pcmdspec = HS_CMDSPEC_CMD::wrap(SRT_CMD_KMRSP) | HS_CMDSPEC_SIZE::wrap((uint32_t) ra_size);
- HLOGC(cnlog.Debug,
- log << CONID() << "createSrtHandshake: KMRSP: applying returned key length="
- << ra_size); // XXX INSECURE << " words: [" << FormatBinaryString((uint8_t*)kmdata,
- // kmdata_wordsize*sizeof(uint32_t)) << "]";
- NtoHLA((space), keydata, ra_size);
- return ra_size;
- }
- // PREREQUISITE:
- // pkt must be set the buffer and configured for UMSG_HANDSHAKE.
- // Note that this function replaces also serialization for the HSv4.
- bool srt::CUDT::createSrtHandshake(
- int srths_cmd,
- int srtkm_cmd,
- const uint32_t* kmdata,
- size_t kmdata_wordsize, // IN WORDS, NOT BYTES!!!
- CPacket& w_pkt,
- CHandShake& w_hs)
- {
- // This function might be called before the opposite version was recognized.
- // Check if the version is exactly 4 because this means that the peer has already
- // sent something - asynchronously, and usually in rendezvous - and we already know
- // that the peer is version 4. In this case, agent must behave as HSv4, til the end.
- if (m_ConnRes.m_iVersion == HS_VERSION_UDT4)
- {
- w_hs.m_iVersion = HS_VERSION_UDT4;
- w_hs.m_iType = UDT_DGRAM;
- if (w_hs.m_extension)
- {
- // Should be impossible
- LOGC(cnlog.Error,
- log << CONID() << "createSrtHandshake: IPE: EXTENSION SET WHEN peer reports version 4 - fixing...");
- w_hs.m_extension = false;
- }
- }
- else
- {
- w_hs.m_iType = 0; // Prepare it for flags
- }
- HLOGC(cnlog.Debug,
- log << CONID() << "createSrtHandshake: buf size=" << w_pkt.getLength()
- << " hsx=" << MessageTypeStr(UMSG_EXT, srths_cmd) << " kmx=" << MessageTypeStr(UMSG_EXT, srtkm_cmd)
- << " kmdata_wordsize=" << kmdata_wordsize << " version=" << w_hs.m_iVersion);
- // Once you are certain that the version is HSv5, set the enc type flags
- // to advertise pbkeylen. Otherwise make sure that the old interpretation
- // will correctly pick up the type field. PBKEYLEN should be advertized
- // regardless of what URQ stage the handshake is (note that in case of rendezvous
- // CONCLUSION might be the FIRST MESSAGE EVER RECEIVED by a party).
- if (w_hs.m_iVersion > HS_VERSION_UDT4)
- {
- // Check if there was a failure to receie HSREQ before trying to craft HSRSP.
- // If fillSrtHandshake_HSRSP catches the condition of m_tsRcvPeerStartTime == steady_clock::zero(),
- // it will return size 0, which will mess up with further extension procedures;
- // PREVENT THIS HERE.
- if (w_hs.m_iReqType == URQ_CONCLUSION && srths_cmd == SRT_CMD_HSRSP && is_zero(m_tsRcvPeerStartTime))
- {
- LOGC(cnlog.Error,
- log << CONID()
- << "createSrtHandshake: IPE (non-fatal): Attempting to craft HSRSP without received HSREQ. "
- "BLOCKING extensions.");
- w_hs.m_extension = false;
- }
- // The situation when this function is called without requested extensions
- // is URQ_CONCLUSION in rendezvous mode in some of the transitions.
- // In this case for version 5 just clear the m_iType field, as it has
- // different meaning in HSv5 and contains extension flags.
- //
- // Keep 0 in the SRT_HSTYPE_HSFLAGS field, but still advertise PBKEYLEN
- // in the SRT_HSTYPE_ENCFLAGS field.
- w_hs.m_iType = SrtHSRequest::wrapFlags(false /*no magic in HSFLAGS*/, m_config.iSndCryptoKeyLen);
- IF_HEAVY_LOGGING(bool whether = m_config.iSndCryptoKeyLen != 0);
- HLOGC(cnlog.Debug,
- log << CONID() << "createSrtHandshake: " << (whether ? "" : "NOT ")
- << " Advertising PBKEYLEN - value = " << m_config.iSndCryptoKeyLen);
- // Note: This is required only when sending a HS message without SRT extensions.
- // When this is to be sent with SRT extensions, then KMREQ will be attached here
- // and the PBKEYLEN will be extracted from it. If this is going to attach KMRSP
- // here, it's already too late (it should've been advertised before getting the first
- // handshake message with KMREQ).
- }
- else
- {
- w_hs.m_iType = UDT_DGRAM;
- }
- // values > URQ_CONCLUSION include also error types
- // if (w_hs.m_iVersion == HS_VERSION_UDT4 || w_hs.m_iReqType > URQ_CONCLUSION) <--- This condition was checked b4 and
- // it's only valid for caller-listener mode
- if (!w_hs.m_extension)
- {
- // Serialize only the basic handshake, if this is predicted for
- // Hsv4 peer or this is URQ_INDUCTION or URQ_WAVEAHAND.
- size_t hs_size = w_pkt.getLength();
- w_hs.store_to((w_pkt.m_pcData), (hs_size));
- w_pkt.setLength(hs_size);
- HLOGC(cnlog.Debug,
- log << CONID() << "createSrtHandshake: (no ext) size=" << hs_size << " data: " << w_hs.show());
- return true;
- }
- // Sanity check, applies to HSv5 only cases.
- if (srths_cmd == SRT_CMD_HSREQ && m_SrtHsSide == HSD_RESPONDER)
- {
- m_RejectReason = SRT_REJ_IPE;
- LOGC(cnlog.Fatal,
- log << CONID() << "IPE: SRT_CMD_HSREQ was requested to be sent in HSv5 by an INITIATOR side!");
- return false; // should cause rejection
- }
- ostringstream logext;
- logext << "HSX";
- // Install the SRT extensions
- w_hs.m_iType |= CHandShake::HS_EXT_HSREQ;
- bool have_sid = false;
- if (srths_cmd == SRT_CMD_HSREQ && !m_config.sStreamName.empty())
- {
- have_sid = true;
- w_hs.m_iType |= CHandShake::HS_EXT_CONFIG;
- logext << ",SID";
- }
- // If this is a response, we have also information
- // on the peer. If Peer is NOT filter capable, don't
- // put filter config, even if agent is capable.
- bool peer_filter_capable = true;
- if (srths_cmd == SRT_CMD_HSRSP)
- {
- if (m_sPeerPktFilterConfigString != "")
- {
- peer_filter_capable = true;
- }
- else if (IsSet(m_uPeerSrtFlags, SRT_OPT_FILTERCAP))
- {
- peer_filter_capable = true;
- }
- else
- {
- peer_filter_capable = false;
- }
- }
- // Now, if this is INITIATOR, then it has its
- // filter config already set, if configured, otherwise
- // it should not attach the filter config extension.
- // If this is a RESPONDER, then it has already received
- // the filter config string from the peer and therefore
- // possibly confronted with the contents of m_OPT_FECConfigString,
- // and if it decided to go with filter, it will be nonempty.
- bool have_filter = false;
- if (peer_filter_capable && !m_config.sPacketFilterConfig.empty())
- {
- have_filter = true;
- w_hs.m_iType |= CHandShake::HS_EXT_CONFIG;
- logext << ",filter";
- }
- bool have_congctl = false;
- const string sm = m_config.sCongestion.str();
- if (sm != "" && sm != "live")
- {
- have_congctl = true;
- w_hs.m_iType |= CHandShake::HS_EXT_CONFIG;
- logext << ",CONGCTL";
- }
- bool have_kmreq = false;
- // Prevent adding KMRSP only in case when BOTH:
- // - Agent has set no password
- // - no KMREQ has arrived from Peer
- // KMRSP must be always sent when:
- // - Agent set a password, Peer did not send KMREQ: Agent sets snd=NOSECRET.
- // - Agent set no password, but Peer sent KMREQ: Ageng sets rcv=NOSECRET.
- if (m_config.CryptoSecret.len > 0 || kmdata_wordsize > 0)
- {
- have_kmreq = true;
- w_hs.m_iType |= CHandShake::HS_EXT_KMREQ;
- logext << ",KMX";
- }
- #if ENABLE_BONDING
- bool have_group = false;
- // Note: this is done without locking because we have the following possibilities:
- //
- // 1. Most positive: the group will be the same all the time up to the moment when we use it.
- // 2. The group will disappear when next time we try to use it having now have_group set true.
- //
- // Not possible that a group is NULL now but would appear later: the group must be either empty
- // or already set as valid at this time.
- //
- // If the 2nd possibility happens, then simply it means that the group has been closed during
- // the operation and the socket got this information updated in the meantime. This means that
- // it was an abnormal interrupt during the processing so the handshake process should be aborted
- // anyway, and that's what will be done.
- // LOCKING INFORMATION: accesing this field just for NULL check doesn't
- // hurt, even if this field could be dangling in the moment. This will be
- // followed by an additional check, done this time under lock, and there will
- // be no dangling pointers at this time.
- if (m_parent->m_GroupOf)
- {
- // Whatever group this socket belongs to, the information about
- // the group is always sent the same way with the handshake.
- have_group = true;
- w_hs.m_iType |= CHandShake::HS_EXT_CONFIG;
- logext << ",GROUP";
- }
- #endif
- HLOGC(cnlog.Debug, log << CONID() << "createSrtHandshake: (ext: " << logext.str() << ") data: " << w_hs.show());
- // NOTE: The HSREQ is practically always required, although may happen
- // in future that CONCLUSION can be sent multiple times for a separate
- // stream encryption support, and this way it won't enclose HSREQ.
- // Also, KMREQ may occur multiple times.
- // So, initially store the UDT legacy handshake.
- size_t hs_size = w_pkt.getLength(), total_ra_size = (hs_size / sizeof(uint32_t)); // Maximum size of data
- w_hs.store_to((w_pkt.m_pcData), (hs_size)); // hs_size is updated
- size_t ra_size = hs_size / sizeof(int32_t);
- // Now attach the SRT handshake for HSREQ
- size_t offset = ra_size;
- uint32_t *p = reinterpret_cast<uint32_t *>(w_pkt.m_pcData);
- // NOTE: since this point, ra_size has a size in int32_t elements, NOT BYTES.
- // The first 4-byte item is the CMD/LENGTH spec.
- uint32_t *pcmdspec = p + offset; // Remember the location to be filled later, when we know the length
- ++offset;
- // Now use the original function to store the actual SRT_HS data
- // ra_size after that
- // NOTE: so far, ra_size is m_iMaxSRTPayloadSize expressed in number of elements.
- // WILL BE CHANGED HERE.
- ra_size = fillSrtHandshake((p + offset), total_ra_size - offset, srths_cmd, HS_VERSION_SRT1);
- *pcmdspec = HS_CMDSPEC_CMD::wrap(srths_cmd) | HS_CMDSPEC_SIZE::wrap((uint32_t) ra_size);
- HLOGC(cnlog.Debug,
- log << CONID() << "createSrtHandshake: after HSREQ: offset=" << offset << " HSREQ size=" << ra_size
- << " space left: " << (total_ra_size - offset));
- // Use only in REQ phase and only if stream name is set
- if (have_sid)
- {
- // Now prepare the string with 4-byte alignment. The string size is limited
- // to half the payload size. Just a sanity check to not pack too much into
- // the conclusion packet.
- size_t size_limit = m_iMaxSRTPayloadSize / 2;
- if (m_config.sStreamName.size() >= size_limit)
- {
- m_RejectReason = SRT_REJ_ROGUE;
- LOGC(cnlog.Warn,
- log << CONID() << "createSrtHandshake: stream id too long, limited to " << (size_limit - 1)
- << " bytes");
- return false;
- }
- offset += ra_size + 1;
- ra_size = fillHsExtConfigString(p + offset - 1, SRT_CMD_SID, m_config.sStreamName.str());
- HLOGC(cnlog.Debug,
- log << CONID() << "createSrtHandshake: after SID [" << m_config.sStreamName.c_str()
- << "] length=" << m_config.sStreamName.size() << " alignedln=" << (4 * ra_size)
- << ": offset=" << offset << " SID size=" << ra_size << " space left: " << (total_ra_size - offset));
- }
- if (have_congctl)
- {
- // Pass the congctl to the other side as informational.
- // The other side should reject connection if it uses a different congctl.
- // The other side should also respond with the congctl it uses, if its non-default (for backward compatibility).
- offset += ra_size + 1;
- ra_size = fillHsExtConfigString(p + offset - 1, SRT_CMD_CONGESTION, sm);
- HLOGC(cnlog.Debug,
- log << CONID() << "createSrtHandshake: after CONGCTL [" << sm << "] length=" << sm.size()
- << " alignedln=" << (4 * ra_size) << ": offset=" << offset << " CONGCTL size=" << ra_size
- << " space left: " << (total_ra_size - offset));
- }
- if (have_filter)
- {
- offset += ra_size + 1;
- ra_size = fillHsExtConfigString(p + offset - 1, SRT_CMD_FILTER, m_config.sPacketFilterConfig.str());
- HLOGC(cnlog.Debug,
- log << CONID() << "createSrtHandshake: after filter [" << m_config.sPacketFilterConfig.c_str()
- << "] length=" << m_config.sPacketFilterConfig.size() << " alignedln=" << (4 * ra_size) << ": offset="
- << offset << " filter size=" << ra_size << " space left: " << (total_ra_size - offset));
- }
- #if ENABLE_BONDING
- // Note that this will fire in both cases:
- // - When the group has been set by the user on a socket (or socket was created as a part of the group),
- // and the handshake request is to be sent with informing the peer that this conenction belongs to a group
- // - When the agent received a HS request with a group, has created its mirror group on its side, and
- // now sends the HS response to the peer, with ITS OWN group id (the mirror one).
- //
- // XXX Probably a condition should be checked here around the group type.
- // The time synchronization should be done only on any kind of parallel sending group.
- // Currently all groups are such groups (broadcast, backup, balancing), but it may
- // need to be changed for some other types.
- if (have_group)
- {
- // NOTE: See information about mutex ordering in api.h
- ScopedLock gdrg (uglobal().m_GlobControlLock);
- if (!m_parent->m_GroupOf)
- {
- // This may only happen if since last check of m_GroupOf pointer the socket was removed
- // from the group in the meantime, which can only happen due to that the group was closed.
- // In such a case it simply means that the handshake process was requested to be interrupted.
- LOGC(cnlog.Fatal, log << CONID() << "GROUP DISAPPEARED. Socket not capable of continuing HS");
- return false;
- }
- else
- {
- if (m_parent->m_GroupOf->closing())
- {
- m_RejectReason = SRT_REJ_IPE;
- LOGC(cnlog.Error,
- log << CONID() << "createSrtHandshake: group is closing during the process, rejecting.");
- return false;
- }
- offset += ra_size + 1;
- ra_size = fillHsExtGroup(p + offset - 1);
- HLOGC(cnlog.Debug,
- log << CONID() << "createSrtHandshake: after GROUP [" << sm << "] length=" << sm.size() << ": offset="
- << offset << " GROUP size=" << ra_size << " space left: " << (total_ra_size - offset));
- }
- }
- #endif
- // When encryption turned on
- if (have_kmreq)
- {
- HLOGC(cnlog.Debug,
- log << CONID() << "createSrtHandshake: "
- << (m_config.CryptoSecret.len > 0 ? "Agent uses ENCRYPTION" : "Peer requires ENCRYPTION"));
- if (!m_pCryptoControl && (srtkm_cmd == SRT_CMD_KMREQ || srtkm_cmd == SRT_CMD_KMRSP))
- {
- m_RejectReason = SRT_REJ_IPE;
- LOGC(cnlog.Error,
- log << CONID() << "createSrtHandshake: IPE: need to send KM, but CryptoControl does not exist."
- << " Socket state: connected=" << boolalpha << m_bConnected << ", connecting=" << m_bConnecting
- << ", broken=" << m_bBroken << ", closing=" << m_bClosing << ".");
- return false;
- }
- if (srtkm_cmd == SRT_CMD_KMREQ)
- {
- bool have_any_keys = false;
- for (size_t ki = 0; ki < 2; ++ki)
- {
- // Skip those that have expired
- if (!m_pCryptoControl->getKmMsg_needSend(ki, false))
- continue;
- m_pCryptoControl->getKmMsg_markSent(ki, false);
- offset += ra_size + 1;
- ra_size = fillHsExtKMREQ(p + offset - 1, ki);
- have_any_keys = true;
- }
- if (!have_any_keys)
- {
- m_RejectReason = SRT_REJ_IPE;
- LOGC(cnlog.Error, log << CONID() << "createSrtHandshake: IPE: all keys have expired, no KM to send.");
- return false;
- }
- }
- else if (srtkm_cmd == SRT_CMD_KMRSP)
- {
- offset += ra_size + 1;
- ra_size = fillHsExtKMRSP(p + offset - 1, kmdata, kmdata_wordsize);
- }
- else
- {
- m_RejectReason = SRT_REJ_IPE;
- LOGC(cnlog.Fatal, log << CONID() << "createSrtHandshake: IPE: wrong value of srtkm_cmd: " << srtkm_cmd);
- return false;
- }
- }
- if (ra_size == 0)
- {
- // m_RejectReason is expected to be set by fillHsExtKMRSP(..) in this case.
- return false;
- }
- // ra_size + offset has a value in element unit.
- // Switch it again to byte unit.
- w_pkt.setLength((ra_size + offset) * sizeof(int32_t));
- HLOGC(cnlog.Debug,
- log << CONID() << "createSrtHandshake: filled HSv5 handshake flags: "
- << CHandShake::ExtensionFlagStr(w_hs.m_iType) << " length: " << w_pkt.getLength() << " bytes");
- return true;
- }
- template <class Integer>
- static inline int FindExtensionBlock(Integer* begin, size_t total_length,
- size_t& w_out_len, Integer*& w_next_block)
- {
- // Check if there's anything to process
- if (total_length == 0)
- {
- w_next_block = NULL;
- w_out_len = 0;
- return SRT_CMD_NONE;
- }
- // This function extracts the block command from the block and its length.
- // The command value is returned as a function result.
- // The size of that command block is stored into w_out_len.
- // The beginning of the prospective next block is stored in w_next_block.
- // The caller must be aware that:
- // - exactly one element holds the block header (cmd+size), so the actual data are after this one.
- // - the returned size is the number of uint32_t elements since that first data element
- // - the remaining size should be manually calculated as total_length - 1 - w_out_len, or
- // simply, as w_next_block - begin.
- // Note that if the total_length is too short to extract the whole block, it will return
- // SRT_CMD_NONE. Note that total_length includes this first CMDSPEC word.
- //
- // When SRT_CMD_NONE is returned, it means that nothing has been extracted and nothing else
- // can be further extracted from this block.
- int cmd = HS_CMDSPEC_CMD::unwrap(*begin);
- size_t size = HS_CMDSPEC_SIZE::unwrap(*begin);
- if (size + 1 > total_length)
- return SRT_CMD_NONE;
- w_out_len = size;
- if (total_length == size + 1)
- w_next_block = NULL;
- else
- w_next_block = begin + 1 + size;
- return cmd;
- }
- // NOTE: the rule of order of arguments is broken here because this order
- // serves better the logics and readability.
- template <class Integer>
- static inline bool NextExtensionBlock(Integer*& w_begin, Integer* next, size_t& w_length)
- {
- if (!next)
- return false;
- w_length = w_length - (next - w_begin);
- w_begin = next;
- return true;
- }
- void SrtExtractHandshakeExtensions(const char* bufbegin, size_t buflength,
- vector<SrtHandshakeExtension>& w_output)
- {
- const uint32_t *begin = reinterpret_cast<const uint32_t *>(bufbegin + CHandShake::m_iContentSize);
- size_t size = buflength - CHandShake::m_iContentSize; // Due to previous cond check we grant it's >0
- const uint32_t *next = 0;
- size_t length = size / sizeof(uint32_t);
- size_t blocklen = 0;
- for (;;) // ONE SHOT, but continuable loop
- {
- const int cmd = FindExtensionBlock(begin, length, (blocklen), (next));
- if (cmd == SRT_CMD_NONE)
- {
- // End of blocks
- break;
- }
- w_output.push_back(SrtHandshakeExtension(cmd));
- SrtHandshakeExtension& ext = w_output.back();
- std::copy(begin+1, begin+blocklen+1, back_inserter(ext.contents));
- // Any other kind of message extracted. Search on.
- if (!NextExtensionBlock((begin), next, (length)))
- break;
- }
- }
- #if SRT_DEBUG_RTT
- class RttTracer
- {
- public:
- RttTracer()
- {
- }
- ~RttTracer()
- {
- srt::sync::ScopedLock lck(m_mtx);
- m_fout.close();
- }
- void trace(const srt::sync::steady_clock::time_point& currtime,
- const std::string& event, int rtt_sample, int rttvar_sample,
- bool is_smoothed_rtt_reset, int64_t recvTotal,
- int smoothed_rtt, int rttvar)
- {
- srt::sync::ScopedLock lck(m_mtx);
- create_file();
-
- m_fout << srt::sync::FormatTimeSys(currtime) << ",";
- m_fout << srt::sync::FormatTime(currtime) << ",";
- m_fout << event << ",";
- m_fout << rtt_sample << ",";
- m_fout << rttvar_sample << ",";
- m_fout << is_smoothed_rtt_reset << ",";
- m_fout << recvTotal << ",";
- m_fout << smoothed_rtt << ",";
- m_fout << rttvar << "\n";
- m_fout.flush();
- }
- private:
- void print_header()
- {
- m_fout << "Timepoint_SYST,Timepoint_STDY,Event,usRTTSample,"
- "usRTTVarSample,IsSmoothedRTTReset,pktsRecvTotal,"
- "usSmoothedRTT,usRTTVar\n";
- }
- void create_file()
- {
- if (m_fout.is_open())
- return;
- std::string str_tnow = srt::sync::FormatTimeSys(srt::sync::steady_clock::now());
- str_tnow.resize(str_tnow.size() - 7); // remove trailing ' [SYST]' part
- while (str_tnow.find(':') != std::string::npos) {
- str_tnow.replace(str_tnow.find(':'), 1, 1, '_');
- }
- const std::string fname = "rtt_trace_" + str_tnow + "_" + SRT_SYNC_CLOCK_STR + ".csv";
- m_fout.open(fname, std::ofstream::out);
- if (!m_fout)
- std::cerr << "IPE: Failed to open " << fname << "!!!\n";
- print_header();
- }
- private:
- srt::sync::Mutex m_mtx;
- std::ofstream m_fout;
- };
- RttTracer s_rtt_trace;
- #endif
- bool srt::CUDT::processSrtMsg(const CPacket *ctrlpkt)
- {
- uint32_t *srtdata = (uint32_t *)ctrlpkt->m_pcData;
- size_t len = ctrlpkt->getLength();
- int etype = ctrlpkt->getExtendedType();
- uint32_t ts = ctrlpkt->m_iTimeStamp;
- int res = SRT_CMD_NONE;
- HLOGC(cnlog.Debug,
- log << CONID() << "Dispatching message type=" << etype << " data length=" << (len / sizeof(int32_t)));
- switch (etype)
- {
- case SRT_CMD_HSREQ:
- {
- res = processSrtMsg_HSREQ(srtdata, len, ts, CUDT::HS_VERSION_UDT4);
- break;
- }
- case SRT_CMD_HSRSP:
- {
- res = processSrtMsg_HSRSP(srtdata, len, ts, CUDT::HS_VERSION_UDT4);
- break;
- }
- case SRT_CMD_KMREQ:
- // Special case when the data need to be processed here
- // and the appropriate message must be constructed for sending.
- // No further processing required
- {
- uint32_t srtdata_out[SRTDATA_MAXSIZE];
- size_t len_out = 0;
- res = m_pCryptoControl->processSrtMsg_KMREQ(srtdata, len, CUDT::HS_VERSION_UDT4,
- (srtdata_out), (len_out));
- if (res == SRT_CMD_KMRSP)
- {
- if (len_out == 1)
- {
- if (m_config.bEnforcedEnc)
- {
- LOGC(cnlog.Warn,
- log << CONID() << "KMREQ FAILURE: " << KmStateStr(SRT_KM_STATE(srtdata_out[0]))
- << " - rejecting per enforced encryption");
- res = SRT_CMD_NONE;
- break;
- }
- HLOGC(cnlog.Debug,
- log << CONID()
- << "MKREQ -> KMRSP FAILURE state: " << KmStateStr(SRT_KM_STATE(srtdata_out[0])));
- }
- else
- {
- HLOGC(cnlog.Debug, log << CONID() << "KMREQ -> requested to send KMRSP length=" << len_out);
- }
- sendSrtMsg(SRT_CMD_KMRSP, srtdata_out, len_out);
- }
- // XXX Dead code. processSrtMsg_KMREQ now doesn't return any other value now.
- // Please review later.
- else
- {
- LOGC(cnlog.Warn, log << CONID() << "KMREQ failed to process the request - ignoring");
- }
- return true; // already done what's necessary
- }
- case SRT_CMD_KMRSP:
- {
- // KMRSP doesn't expect any following action
- m_pCryptoControl->processSrtMsg_KMRSP(srtdata, len, CUDT::HS_VERSION_UDT4);
- return true; // nothing to do
- }
- default:
- return false;
- }
- if (res == SRT_CMD_NONE)
- return true;
- // Send the message that the message handler requested.
- sendSrtMsg(res);
- return true;
- }
- int srt::CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint32_t ts, int hsv)
- {
- // Set this start time in the beginning, regardless as to whether TSBPD is being
- // used or not. This must be done in the Initiator as well as Responder.
- /*
- * Compute peer StartTime in our time reference
- * This takes time zone, time drift into account.
- * Also includes current packet transit time (rtt/2)
- */
- m_tsRcvPeerStartTime = steady_clock::now() - microseconds_from(ts);
- // (in case of bonding group, this value will be OVERWRITTEN
- // later in CUDT::interpretGroup).
- // Prepare the initial runtime values of latency basing on the option values.
- // They are going to get the value fixed HERE.
- m_iTsbPdDelay_ms = m_config.iRcvLatency;
- m_iPeerTsbPdDelay_ms = m_config.iPeerLatency;
- if (bytelen < SRT_CMD_HSREQ_MINSZ)
- {
- m_RejectReason = SRT_REJ_ROGUE;
- /* Packet smaller than minimum compatible packet size */
- LOGC(cnlog.Error, log << "HSREQ/rcv: cmd=" << SRT_CMD_HSREQ << "(HSREQ) len=" << bytelen << " invalid");
- return SRT_CMD_NONE;
- }
- LOGC(cnlog.Note, log << "HSREQ/rcv: cmd=" << SRT_CMD_HSREQ << "(HSREQ) len=" << bytelen
- << hex << " vers=0x" << srtdata[SRT_HS_VERSION] << " opts=0x" << srtdata[SRT_HS_FLAGS]
- << dec << " delay=" << SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY]));
- m_uPeerSrtVersion = srtdata[SRT_HS_VERSION];
- m_uPeerSrtFlags = srtdata[SRT_HS_FLAGS];
- if (hsv == CUDT::HS_VERSION_UDT4)
- {
- if (m_uPeerSrtVersion >= SRT_VERSION_FEAT_HSv5)
- {
- m_RejectReason = SRT_REJ_ROGUE;
- LOGC(cnlog.Error,
- log << CONID() << "HSREQ/rcv: With HSv4 version >= " << SrtVersionString(SRT_VERSION_FEAT_HSv5)
- << " is not acceptable.");
- return SRT_CMD_REJECT;
- }
- }
- else
- {
- if (m_uPeerSrtVersion < SRT_VERSION_FEAT_HSv5)
- {
- m_RejectReason = SRT_REJ_ROGUE;
- LOGC(cnlog.Error,
- log << CONID() << "HSREQ/rcv: With HSv5 version must be >= " << SrtVersionString(SRT_VERSION_FEAT_HSv5)
- << " .");
- return SRT_CMD_REJECT;
- }
- }
- // Check also if the version satisfies the minimum required version
- if (m_uPeerSrtVersion < m_config.uMinimumPeerSrtVersion)
- {
- m_RejectReason = SRT_REJ_VERSION;
- LOGC(cnlog.Error,
- log << CONID() << "HSREQ/rcv: Peer version: " << SrtVersionString(m_uPeerSrtVersion)
- << " is too old for requested: " << SrtVersionString(m_config.uMinimumPeerSrtVersion)
- << " - REJECTING");
- return SRT_CMD_REJECT;
- }
- HLOGC(cnlog.Debug,
- log << CONID() << "HSREQ/rcv: PEER Version: " << SrtVersionString(m_uPeerSrtVersion)
- << " Flags: " << m_uPeerSrtFlags << "(" << SrtFlagString(m_uPeerSrtFlags)
- << ") Min req version:" << SrtVersionString(m_config.uMinimumPeerSrtVersion));
- m_bPeerRexmitFlag = IsSet(m_uPeerSrtFlags, SRT_OPT_REXMITFLG);
- HLOGC(cnlog.Debug, log << CONID() << "HSREQ/rcv: peer " << (m_bPeerRexmitFlag ? "UNDERSTANDS" : "DOES NOT UNDERSTAND") << " REXMIT flag");
- // Check if both use the same API type. Reject if not.
- bool peer_message_api = !IsSet(m_uPeerSrtFlags, SRT_OPT_STREAM);
- if (peer_message_api != m_config.bMessageAPI)
- {
- m_RejectReason = SRT_REJ_MESSAGEAPI;
- LOGC(cnlog.Error,
- log << CONID() << "HSREQ/rcv: Agent uses " << (m_config.bMessageAPI ? "MESSAGE" : "STREAM")
- << " API, but the Peer declares " << (peer_message_api ? "MESSAGE" : "STREAM")
- << " API. Not compatible transmission type, rejecting.");
- return SRT_CMD_REJECT;
- }
- SRT_STATIC_ASSERT(SRT_HS_E_SIZE == SRT_HS_LATENCY + 1, "Assuming latency is the last field");
- if (bytelen < (SRT_HS_E_SIZE * sizeof(uint32_t)))
- {
- // Handshake extension message includes VERSION, FLAGS and LATENCY
- // (3 x 32 bits). SRT v1.2.0 and earlier might supply shorter extension message,
- // without LATENCY fields.
- // It is acceptable, as long as the latency flags are not set on our side.
- //
- // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | SRT Version |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | SRT Flags |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | Receiver TSBPD Delay | Sender TSBPD Delay |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- if (IsSet(m_uPeerSrtFlags, SRT_OPT_TSBPDSND) || IsSet(m_uPeerSrtFlags, SRT_OPT_TSBPDRCV))
- {
- m_RejectReason = SRT_REJ_ROGUE;
- LOGC(cnlog.Error,
- log << CONID()
- << "HSREQ/rcv: Peer sent only VERSION + FLAGS HSREQ, but TSBPD flags are set. Rejecting.");
- return SRT_CMD_REJECT;
- }
- LOGC(cnlog.Warn,
- log << CONID() << "HSREQ/rcv: Peer sent only VERSION + FLAGS HSREQ, not getting any TSBPD settings.");
- // Don't process any further settings in this case. Turn off TSBPD, just for a case.
- m_bTsbPd = false;
- m_bPeerTsbPd = false;
- return SRT_CMD_HSRSP;
- }
- const uint32_t latencystr = srtdata[SRT_HS_LATENCY];
- if (IsSet(m_uPeerSrtFlags, SRT_OPT_TSBPDSND))
- {
- // TimeStamp-based Packet Delivery feature enabled
- if (!isOPT_TsbPd())
- {
- LOGC(cnlog.Warn,
- log << CONID() << "HSREQ/rcv: Agent did not set rcv-TSBPD - ignoring proposed latency from peer");
- // Note: also don't set the peer TSBPD flag HERE because
- // - in HSv4 it will be a sender, so it doesn't matter anyway
- // - in HSv5 if it's going to receive, the TSBPDRCV flag will define it.
- }
- else
- {
- int peer_decl_latency;
- if (hsv < CUDT::HS_VERSION_SRT1)
- {
- // In HSv4 there is only one value and this is the latency
- // that the sender peer proposes for the agent.
- peer_decl_latency = SRT_HS_LATENCY_LEG::unwrap(latencystr);
- }
- else
- {
- // In HSv5 there are latency declared for sending and receiving separately.
- // SRT_HS_LATENCY_SND is the value that the peer proposes to be the
- // value used by agent when receiving data. We take this as a local latency value.
- peer_decl_latency = SRT_HS_LATENCY_SND::unwrap(srtdata[SRT_HS_LATENCY]);
- }
- // Use the maximum latency out of latency from our settings and the latency
- // "proposed" by the peer.
- int maxdelay = std::max(m_iTsbPdDelay_ms, peer_decl_latency);
- HLOGC(cnlog.Debug,
- log << CONID() << "HSREQ/rcv: LOCAL/RCV LATENCY: Agent:" << m_iTsbPdDelay_ms
- << " Peer:" << peer_decl_latency << " Selecting:" << maxdelay);
- m_iTsbPdDelay_ms = maxdelay;
- m_bTsbPd = true;
- }
- }
- else
- {
- std::string how_about_agent = isOPT_TsbPd() ? "BUT AGENT DOES" : "and nor does Agent";
- HLOGC(cnlog.Debug, log << CONID() << "HSREQ/rcv: Peer DOES NOT USE latency for sending - " << how_about_agent);
- }
- // This happens when the HSv5 RESPONDER receives the HSREQ message; it declares
- // that the peer INITIATOR will receive the data and informs about its predefined
- // latency. We need to maximize this with our setting of the peer's latency and
- // record as peer's latency, which will be then sent back with HSRSP.
- if (hsv > CUDT::HS_VERSION_UDT4 && IsSet(m_uPeerSrtFlags, SRT_OPT_TSBPDRCV))
- {
- // So, PEER uses TSBPD, set the flag.
- // NOTE: it doesn't matter, if AGENT uses TSBPD.
- m_bPeerTsbPd = true;
- // SRT_HS_LATENCY_RCV is the value that the peer declares as to be
- // used by it when receiving data. We take this as a peer's value,
- // and select the maximum of this one and our proposed latency for the peer.
- int peer_decl_latency = SRT_HS_LATENCY_RCV::unwrap(latencystr);
- int maxdelay = std::max(m_iPeerTsbPdDelay_ms, peer_decl_latency);
- HLOGC(cnlog.Debug,
- log << CONID() << "HSREQ/rcv: PEER/RCV LATENCY: Agent:" << m_iPeerTsbPdDelay_ms
- << " Peer:" << peer_decl_latency << " Selecting:" << maxdelay);
- m_iPeerTsbPdDelay_ms = maxdelay;
- }
- else
- {
- std::string how_about_agent = isOPT_TsbPd() ? "BUT AGENT DOES" : "and nor does Agent";
- HLOGC(cnlog.Debug,
- log << CONID() << "HSREQ/rcv: Peer DOES NOT USE latency for receiving - " << how_about_agent);
- }
- if (hsv > CUDT::HS_VERSION_UDT4)
- {
- // This is HSv5, do the same things as required for the sending party in HSv4,
- // as in HSv5 this can also be a sender.
- if (IsSet(m_uPeerSrtFlags, SRT_OPT_TLPKTDROP))
- {
- // Too late packets dropping feature supported
- m_bPeerTLPktDrop = true;
- }
- if (IsSet(m_uPeerSrtFlags, SRT_OPT_NAKREPORT))
- {
- // Peer will send Periodic NAK Reports
- m_bPeerNakReport = true;
- }
- }
- return SRT_CMD_HSRSP;
- }
- int srt::CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint32_t ts, int hsv)
- {
- // XXX Check for mis-version
- // With HSv4 we accept only version less than 1.3.0
- if (hsv == CUDT::HS_VERSION_UDT4 && srtdata[SRT_HS_VERSION] >= SRT_VERSION_FEAT_HSv5)
- {
- LOGC(cnlog.Error, log << CONID() << "HSRSP/rcv: With HSv4 version >= 1.2.0 is not acceptable.");
- return SRT_CMD_NONE;
- }
- if (bytelen < SRT_CMD_HSRSP_MINSZ)
- {
- /* Packet smaller than minimum compatible packet size */
- LOGC(cnlog.Error, log << CONID() << "HSRSP/rcv: cmd=" << SRT_CMD_HSRSP << "(HSRSP) len=" << bytelen << " invalid");
- return SRT_CMD_NONE;
- }
- // Set this start time in the beginning, regardless as to whether TSBPD is being
- // used or not. This must be done in the Initiator as well as Responder. In case when
- // agent is sender only (HSv4) this value simply won't be used.
- /*
- * Compute peer StartTime in our time reference
- * This takes time zone, time drift into account.
- * Also includes current packet transit time (rtt/2)
- */
- if (is_zero(m_tsRcvPeerStartTime))
- {
- // Do not set this time when it's already set, which may be the case
- // if the agent has this value already "borrowed" from a master socket
- // that was in the group at the time when it was added.
- m_tsRcvPeerStartTime = steady_clock::now() - microseconds_from(ts);
- HLOGC(cnlog.Debug,
- log << CONID()
- << "HSRSP/rcv: PEER START TIME not yet defined, setting: " << FormatTime(m_tsRcvPeerStartTime));
- }
- else
- {
- HLOGC(cnlog.Debug,
- log << CONID()
- << "HSRSP/rcv: PEER START TIME already set (derived): " << FormatTime(m_tsRcvPeerStartTime));
- }
- m_uPeerSrtVersion = srtdata[SRT_HS_VERSION];
- m_uPeerSrtFlags = srtdata[SRT_HS_FLAGS];
- HLOGC(cnlog.Debug, log << "HSRSP/rcv: Version: " << SrtVersionString(m_uPeerSrtVersion)
- << " Flags: SND:" << setw(8) << setfill('0') << hex << m_uPeerSrtFlags
- << setw(0) << " (" << SrtFlagString(m_uPeerSrtFlags) << ")");
- // Basic version check
- if (m_uPeerSrtVersion < m_config.uMinimumPeerSrtVersion)
- {
- m_RejectReason = SRT_REJ_VERSION;
- LOGC(cnlog.Error,
- log << CONID() << "HSRSP/rcv: Peer version: " << SrtVersionString(m_uPeerSrtVersion)
- << " is too old for requested: " << SrtVersionString(m_config.uMinimumPeerSrtVersion)
- << " - REJECTING");
- return SRT_CMD_REJECT;
- }
- if (hsv == CUDT::HS_VERSION_UDT4)
- {
- // The old HSv4 way: extract just one value and put it under peer.
- if (IsSet(m_uPeerSrtFlags, SRT_OPT_TSBPDRCV))
- {
- // TsbPd feature enabled
- m_bPeerTsbPd = true;
- m_iPeerTsbPdDelay_ms = SRT_HS_LATENCY_LEG::unwrap(srtdata[SRT_HS_LATENCY]);
- HLOGC(cnlog.Debug,
- log << CONID() << "HSRSP/rcv: LATENCY: Peer/snd:" << m_iPeerTsbPdDelay_ms
- << " (Agent: declared:" << m_iTsbPdDelay_ms << " rcv:" << m_iTsbPdDelay_ms << ")");
- }
- // TSBPDSND isn't set in HSv4 by the RESPONDER, because HSv4 RESPONDER is always RECEIVER.
- }
- else
- {
- // HSv5 way: extract the receiver latency and sender latency, if used.
- // PEER WILL RECEIVE TSBPD == AGENT SHALL SEND TSBPD.
- if (IsSet(m_uPeerSrtFlags, SRT_OPT_TSBPDRCV))
- {
- // TsbPd feature enabled
- m_bPeerTsbPd = true;
- m_iPeerTsbPdDelay_ms = SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY]);
- HLOGC(cnlog.Debug, log << CONID() << "HSRSP/rcv: LATENCY: Peer/snd:" << m_iPeerTsbPdDelay_ms << "ms");
- }
- else
- {
- HLOGC(cnlog.Debug, log << CONID() << "HSRSP/rcv: Peer (responder) DOES NOT USE latency");
- }
- // PEER WILL SEND TSBPD == AGENT SHALL RECEIVE TSBPD.
- if (IsSet(m_uPeerSrtFlags, SRT_OPT_TSBPDSND))
- {
- if (!isOPT_TsbPd())
- {
- LOGC(cnlog.Warn,
- log << CONID()
- << "HSRSP/rcv: BUG? Peer (responder) declares sending latency, but Agent turned off TSBPD.");
- }
- else
- {
- m_bTsbPd = true; // NOTE: in case of Group TSBPD receiving, this field will be SWITCHED TO m_bGroupTsbPd.
- // Take this value as a good deal. In case when the Peer did not "correct" the latency
- // because it has TSBPD turned off, just stay with the present value defined in options.
- m_iTsbPdDelay_ms = SRT_HS_LATENCY_SND::unwrap(srtdata[SRT_HS_LATENCY]);
- HLOGC(cnlog.Debug, log << CONID() << "HSRSP/rcv: LATENCY Agent/rcv: " << m_iTsbPdDelay_ms << "ms");
- }
- }
- }
- if ((m_config.uSrtVersion >= SrtVersion(1, 0, 5)) && IsSet(m_uPeerSrtFlags, SRT_OPT_TLPKTDROP))
- {
- // Too late packets dropping feature supported
- m_bPeerTLPktDrop = true;
- }
- if ((m_config.uSrtVersion >= SrtVersion(1, 1, 0)) && IsSet(m_uPeerSrtFlags, SRT_OPT_NAKREPORT))
- {
- // Peer will send Periodic NAK Reports
- m_bPeerNakReport = true;
- }
- if (m_config.uSrtVersion >= SrtVersion(1, 2, 0))
- {
- if (IsSet(m_uPeerSrtFlags, SRT_OPT_REXMITFLG))
- {
- // Peer will use REXMIT flag in packet retransmission.
- m_bPeerRexmitFlag = true;
- HLOGP(cnlog.Debug, "HSRSP/rcv: 1.2.0+ Agent understands REXMIT flag and so does peer.");
- }
- else
- {
- HLOGP(cnlog.Debug, "HSRSP/rcv: Agent understands REXMIT flag, but PEER DOES NOT");
- }
- }
- else
- {
- HLOGP(cnlog.Debug, "HSRSP/rcv: <1.2.0 Agent DOESN'T understand REXMIT flag");
- }
- handshakeDone();
- return SRT_CMD_NONE;
- }
- // This function is called only when the URQ_CONCLUSION handshake has been received from the peer.
- bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs,
- const CPacket& hspkt,
- uint32_t* out_data SRT_ATR_UNUSED,
- size_t* pw_len)
- {
- // Initialize pw_len to 0 to handle the unencrypted case
- if (pw_len)
- *pw_len = 0;
- // The version=0 statement as rejection is used only since HSv5.
- // The HSv4 sends the AGREEMENT handshake message with version=0, do not misinterpret it.
- if (m_ConnRes.m_iVersion > HS_VERSION_UDT4 && hs.m_iVersion == 0)
- {
- m_RejectReason = SRT_REJ_PEER;
- LOGC(cnlog.Error, log << CONID() << "HS VERSION = 0, meaning the handshake has been rejected.");
- return false;
- }
- if (hs.m_iVersion < HS_VERSION_SRT1)
- {
- if (m_config.uMinimumPeerSrtVersion && m_config.uMinimumPeerSrtVersion >= SRT_VERSION_FEAT_HSv5)
- {
- m_RejectReason = SRT_REJ_VERSION;
- // This means that a version with minimum 1.3.0 that features HSv5 is required,
- // hence all HSv4 clients should be rejected.
- LOGP(cnlog.Error, "interpretSrtHandshake: minimum peer version 1.3.0 (HSv5 only), rejecting HSv4 client");
- return false;
- }
- return true; // do nothing
- }
- // Anyway, check if the handshake contains any extra data.
- if (hspkt.getLength() <= CHandShake::m_iContentSize)
- {
- m_RejectReason = SRT_REJ_ROGUE;
- // This would mean that the handshake was at least HSv5, but somehow no extras were added.
- // Dismiss it then, however this has to be logged.
- LOGC(cnlog.Error, log << CONID() << "HS VERSION=" << hs.m_iVersion << " but no handshake extension found!");
- return false;
- }
- // We still believe it should work, let's check the flags.
- const int ext_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(hs.m_iType);
- if (ext_flags == 0)
- {
- m_RejectReason = SRT_REJ_ROGUE;
- LOGC(cnlog.Error,
- log << CONID() << "HS VERSION=" << hs.m_iVersion << " but no handshake extension flags are set!");
- return false;
- }
- HLOGC(cnlog.Debug,
- log << CONID() << "HS VERSION=" << hs.m_iVersion
- << " EXTENSIONS: " << CHandShake::ExtensionFlagStr(ext_flags));
- // Ok, now find the beginning of an int32_t array that follows the UDT handshake.
- uint32_t* p = reinterpret_cast<uint32_t*>(hspkt.m_pcData + CHandShake::m_iContentSize);
- size_t size = hspkt.getLength() - CHandShake::m_iContentSize; // Due to previous cond check we grant it's >0
- int hsreq_type_cmd SRT_ATR_UNUSED = SRT_CMD_NONE;
- if (IsSet(ext_flags, CHandShake::HS_EXT_HSREQ))
- {
- HLOGC(cnlog.Debug, log << CONID() << "interpretSrtHandshake: extracting HSREQ/RSP type extension");
- uint32_t *begin = p;
- uint32_t *next = 0;
- size_t length = size / sizeof(uint32_t);
- size_t blocklen = 0;
- for (;;) // this is ONE SHOT LOOP
- {
- int cmd = FindExtensionBlock(begin, length, (blocklen), (next));
- size_t bytelen = blocklen * sizeof(uint32_t);
- if (cmd == SRT_CMD_HSREQ)
- {
- hsreq_type_cmd = cmd;
- // Set is the size as it should, then give it for interpretation for
- // the proper function.
- if (blocklen < SRT_HS_E_SIZE)
- {
- m_RejectReason = SRT_REJ_ROGUE;
- LOGC(cnlog.Error,
- log << CONID() << "HS-ext HSREQ found but invalid size: " << bytelen
- << " (expected: " << SRT_HS_E_SIZE << ")");
- return false; // don't interpret
- }
- int rescmd = processSrtMsg_HSREQ(begin + 1, bytelen, hspkt.m_iTimeStamp, HS_VERSION_SRT1);
- // Interpreted? Then it should be responded with SRT_CMD_HSRSP.
- if (rescmd != SRT_CMD_HSRSP)
- {
- // m_RejectReason already set
- LOGC(cnlog.Error,
- log << CONID() << "interpretSrtHandshake: process HSREQ returned unexpected value " << rescmd);
- return false;
- }
- handshakeDone();
- // updateAfterSrtHandshake -> moved to postConnect and processRendezvous
- }
- else if (cmd == SRT_CMD_HSRSP)
- {
- hsreq_type_cmd = cmd;
- // Set is the size as it should, then give it for interpretation for
- // the proper function.
- if (blocklen < SRT_HS_E_SIZE)
- {
- m_RejectReason = SRT_REJ_ROGUE;
- LOGC(cnlog.Error,
- log << CONID() << "HS-ext HSRSP found but invalid size: " << bytelen
- << " (expected: " << SRT_HS_E_SIZE << ")");
- return false; // don't interpret
- }
- int rescmd = processSrtMsg_HSRSP(begin + 1, bytelen, hspkt.m_iTimeStamp, HS_VERSION_SRT1);
- // Interpreted? Then it should be responded with SRT_CMD_NONE.
- // (nothing to be responded for HSRSP, unless there was some kinda problem)
- if (rescmd != SRT_CMD_NONE)
- {
- // Just formally; the current code doesn't seem to return anything else
- // (unless it's already set)
- if (m_RejectReason == SRT_REJ_UNKNOWN)
- m_RejectReason = SRT_REJ_ROGUE;
- LOGC(cnlog.Error,
- log << CONID() << "interpretSrtHandshake: process HSRSP returned unexpected value " << rescmd);
- return false;
- }
- handshakeDone();
- // updateAfterSrtHandshake -> moved to postConnect and processRendezvous
- }
- else if (cmd == SRT_CMD_NONE)
- {
- m_RejectReason = SRT_REJ_ROGUE;
- LOGC(cnlog.Warn,
- log << CONID() << "interpretSrtHandshake: no HSREQ/HSRSP block found in the handshake msg!");
- // This means that there can be no more processing done by FindExtensionBlock().
- // And we haven't found what we need - otherwise one of the above cases would pass
- // and lead to exit this loop immediately.
- return false;
- }
- else
- {
- // Any other kind of message extracted. Search on.
- length -= (next - begin);
- begin = next;
- if (begin)
- continue;
- }
- break;
- }
- }
- HLOGC(cnlog.Debug, log << CONID() << "interpretSrtHandshake: HSREQ done, checking KMREQ");
- // Now check the encrypted
- bool encrypted = false;
- if (IsSet(ext_flags, CHandShake::HS_EXT_KMREQ))
- {
- HLOGC(cnlog.Debug, log << CONID() << "interpretSrtHandshake: extracting KMREQ/RSP type extension");
- #ifdef SRT_ENABLE_ENCRYPTION
- if (!m_pCryptoControl->hasPassphrase())
- {
- if (m_config.bEnforcedEnc)
- {
- m_RejectReason = SRT_REJ_UNSECURE;
- LOGC(cnlog.Error,
- log << CONID()
- << "HS KMREQ: Peer declares encryption, but agent does not - rejecting per enforced "
- "encryption");
- return false;
- }
- LOGC(cnlog.Warn,
- log << CONID()
- << "HS KMREQ: Peer declares encryption, but agent does not - still allowing connection.");
- // Still allow for connection, and allow Agent to send unencrypted stream to the peer.
- // Also normally allow the key to be processed; worst case it will send the failure response.
- }
- uint32_t *begin = p;
- uint32_t *next = 0;
- size_t length = size / sizeof(uint32_t);
- size_t blocklen = 0;
- for (;;) // This is one shot loop, unless REPEATED by 'continue'.
- {
- int cmd = FindExtensionBlock(begin, length, (blocklen), (next));
- HLOGC(cnlog.Debug,
- log << CONID() << "interpretSrtHandshake: found extension: (" << cmd << ") "
- << MessageTypeStr(UMSG_EXT, cmd));
- size_t bytelen = blocklen * sizeof(uint32_t);
- if (cmd == SRT_CMD_KMREQ)
- {
- if (!out_data || !pw_len)
- {
- m_RejectReason = SRT_REJ_IPE;
- LOGC(cnlog.Fatal, log << CONID() << "IPE: HS/KMREQ extracted without passing target buffer!");
- return false;
- }
- int res = m_pCryptoControl->processSrtMsg_KMREQ(begin + 1, bytelen, HS_VERSION_SRT1,
- (out_data), (*pw_len));
- if (res != SRT_CMD_KMRSP)
- {
- m_RejectReason = SRT_REJ_IPE;
- // Something went wrong.
- HLOGC(cnlog.Debug,
- log << CONID() << "interpretSrtHandshake: IPE/EPE KMREQ processing failed - returned "
- << res);
- return false;
- }
- if (*pw_len == 1)
- {
- #ifdef ENABLE_AEAD_API_PREVIEW
- if (m_pCryptoControl->m_RcvKmState == SRT_KM_S_BADCRYPTOMODE)
- {
- // Cryptographic modes mismatch. Not acceptable at all.
- m_RejectReason = SRT_REJ_CRYPTO;
- LOGC(cnlog.Error,
- log << CONID()
- << "interpretSrtHandshake: KMREQ result: Bad crypto mode - rejecting");
- return false;
- }
- #endif
- // This means that there was an abnormal encryption situation occurred.
- // This is inacceptable in case of strict encryption.
- if (m_config.bEnforcedEnc)
- {
- if (m_pCryptoControl->m_RcvKmState == SRT_KM_S_BADSECRET)
- {
- m_RejectReason = SRT_REJ_BADSECRET;
- }
- else
- {
- m_RejectReason = SRT_REJ_UNSECURE;
- }
- LOGC(cnlog.Error,
- log << CONID()
- << "interpretSrtHandshake: KMREQ result abnornal - rejecting per enforced encryption");
- return false;
- }
- }
- encrypted = true;
- }
- else if (cmd == SRT_CMD_KMRSP)
- {
- int res = m_pCryptoControl->processSrtMsg_KMRSP(begin + 1, bytelen, HS_VERSION_SRT1);
- if (m_config.bEnforcedEnc && res == -1)
- {
- if (m_pCryptoControl->m_SndKmState == SRT_KM_S_BADSECRET)
- m_RejectReason = SRT_REJ_BADSECRET;
- #ifdef ENABLE_AEAD_API_PREVIEW
- else if (m_pCryptoControl->m_SndKmState == SRT_KM_S_BADCRYPTOMODE)
- m_RejectReason = SRT_REJ_CRYPTO;
- #endif
- else
- m_RejectReason = SRT_REJ_UNSECURE;
- LOGC(cnlog.Error,
- log << CONID() << "KMRSP failed - rejecting connection as per enforced encryption.");
- return false;
- }
- encrypted = true;
- }
- else if (cmd == SRT_CMD_NONE)
- {
- m_RejectReason = SRT_REJ_ROGUE;
- LOGC(cnlog.Error, log << CONID() << "HS KMREQ expected - none found!");
- return false;
- }
- else
- {
- HLOGC(cnlog.Debug, log << CONID() << "interpretSrtHandshake: ... skipping " << MessageTypeStr(UMSG_EXT, cmd));
- if (NextExtensionBlock((begin), next, (length)))
- continue;
- }
- break;
- }
- #else
- // When encryption is not enabled at compile time, behave as if encryption wasn't set,
- // so accordingly to StrictEncryption flag.
- if (m_config.bEnforcedEnc)
- {
- m_RejectReason = SRT_REJ_UNSECURE;
- LOGC(cnlog.Error,
- log << CONID()
- << "HS KMREQ: Peer declares encryption, but agent didn't enable it at compile time - rejecting "
- "per enforced encryption");
- return false;
- }
- LOGC(cnlog.Warn,
- log << CONID()
- << "HS KMREQ: Peer declares encryption, but agent didn't enable it at compile time - still allowing "
- "connection.");
- encrypted = true;
- #endif
- }
- bool have_congctl = false;
- bool have_filter = false;
- string agsm = m_config.sCongestion.str();
- if (agsm == "")
- {
- agsm = "live";
- m_config.sCongestion.set("live", 4);
- }
- bool have_group SRT_ATR_UNUSED = false;
- if (IsSet(ext_flags, CHandShake::HS_EXT_CONFIG))
- {
- HLOGC(cnlog.Debug, log << CONID() << "interpretSrtHandshake: extracting various CONFIG extensions");
- uint32_t *begin = p;
- uint32_t *next = 0;
- size_t length = size / sizeof(uint32_t);
- size_t blocklen = 0;
- for (;;) // This is one shot loop, unless REPEATED by 'continue'.
- {
- int cmd = FindExtensionBlock(begin, length, (blocklen), (next));
- HLOGC(cnlog.Debug,
- log << CONID() << "interpretSrtHandshake: found extension: (" << cmd << ") "
- << MessageTypeStr(UMSG_EXT, cmd));
- const size_t bytelen = blocklen * sizeof(uint32_t);
- if (cmd == SRT_CMD_SID)
- {
- if (!bytelen || bytelen > CSrtConfig::MAX_SID_LENGTH)
- {
- LOGC(cnlog.Error,
- log << CONID() << "interpretSrtHandshake: STREAMID length " << bytelen << " is 0 or > "
- << +CSrtConfig::MAX_SID_LENGTH << " - PROTOCOL ERROR, REJECTING");
- return false;
- }
- // Copied through a cleared array. This is because the length is aligned to 4
- // where the padding is filled by zero bytes. For the case when the string is
- // exactly of a 4-divisible length, we make a big array with maximum allowed size
- // filled with zeros. Copying to this array should then copy either only the valid
- // characters of the string (if the length is divisible by 4), or the string with
- // padding zeros. In all these cases in the resulting array we should have all
- // subsequent characters of the string plus at least one '\0' at the end. This will
- // make it a perfect NUL-terminated string, to be used to initialize a string.
- char target[CSrtConfig::MAX_SID_LENGTH + 1];
- memset((target), 0, CSrtConfig::MAX_SID_LENGTH + 1);
- memcpy((target), begin + 1, bytelen);
- // Un-swap on big endian machines
- ItoHLA((uint32_t *)target, (uint32_t *)target, blocklen);
- m_config.sStreamName.set(target, strlen(target));
- HLOGC(cnlog.Debug,
- log << CONID() << "CONNECTOR'S REQUESTED SID [" << m_config.sStreamName.c_str()
- << "] (bytelen=" << bytelen << " blocklen=" << blocklen << ")");
- }
- else if (cmd == SRT_CMD_CONGESTION)
- {
- if (have_congctl)
- {
- m_RejectReason = SRT_REJ_ROGUE;
- LOGC(cnlog.Error, log << CONID() << "CONGCTL BLOCK REPEATED!");
- return false;
- }
- if (!bytelen || bytelen > CSrtConfig::MAX_CONG_LENGTH)
- {
- LOGC(cnlog.Error,
- log << CONID() << "interpretSrtHandshake: CONGESTION-control type length " << bytelen
- << " is 0 or > " << +CSrtConfig::MAX_CONG_LENGTH << " - PROTOCOL ERROR, REJECTING");
- return false;
- }
- // Declare that congctl has been received
- have_congctl = true;
- char target[CSrtConfig::MAX_CONG_LENGTH + 1];
- memset((target), 0, CSrtConfig::MAX_CONG_LENGTH + 1);
- memcpy((target), begin + 1, bytelen);
- // Un-swap on big endian machines
- ItoHLA((uint32_t *)target, (uint32_t *)target, blocklen);
- string sm = target;
- // As the congctl has been declared by the peer,
- // check if your congctl is compatible.
- // sm cannot be empty, but the agent's sm can be empty meaning live.
- if (sm != agsm)
- {
- m_RejectReason = SRT_REJ_CONGESTION;
- LOGC(cnlog.Error,
- log << CONID() << "PEER'S CONGCTL '" << sm << "' does not match AGENT'S CONGCTL '" << agsm
- << "'");
- return false;
- }
- HLOGC(cnlog.Debug,
- log << CONID() << "CONNECTOR'S CONGCTL [" << sm << "] (bytelen=" << bytelen
- << " blocklen=" << blocklen << ")");
- }
- else if (cmd == SRT_CMD_FILTER)
- {
- if (have_filter)
- {
- m_RejectReason = SRT_REJ_FILTER;
- LOGC(cnlog.Error, log << CONID() << "FILTER BLOCK REPEATED!");
- return false;
- }
- if (!bytelen || bytelen > CSrtConfig::MAX_PFILTER_LENGTH)
- {
- LOGC(cnlog.Error,
- log << CONID() << "interpretSrtHandshake: packet-filter type length " << bytelen
- << " is 0 or > " << +CSrtConfig::MAX_PFILTER_LENGTH << " - PROTOCOL ERROR, REJECTING");
- return false;
- }
- // Declare that filter has been received
- have_filter = true;
- char target[CSrtConfig::MAX_PFILTER_LENGTH + 1];
- memset((target), 0, CSrtConfig::MAX_PFILTER_LENGTH + 1);
- memcpy((target), begin + 1, bytelen);
- // Un-swap on big endian machines
- ItoHLA((uint32_t *)target, (uint32_t *)target, blocklen);
- string fltcfg = target;
- HLOGC(cnlog.Debug,
- log << CONID() << "PEER'S FILTER CONFIG [" << fltcfg << "] (bytelen=" << bytelen
- << " blocklen=" << blocklen << ")");
- if (!checkApplyFilterConfig(fltcfg))
- {
- m_RejectReason = SRT_REJ_FILTER;
- LOGC(cnlog.Error, log << CONID() << "PEER'S FILTER CONFIG [" << fltcfg << "] has been rejected");
- return false;
- }
- }
- #if ENABLE_BONDING
- else if ( cmd == SRT_CMD_GROUP )
- {
- // Note that this will fire in both cases:
- // - When receiving HS request from the Initiator, which belongs to a group, and agent must
- // create the mirror group on his side (or join the existing one, if there's already
- // a mirror group for that group ID).
- // - When receiving HS response from the Responder, with its mirror group ID, so the agent
- // must put the group into his peer group data
- int32_t groupdata[GRPD_E_SIZE] = {};
- if (bytelen < GRPD_MIN_SIZE * GRPD_FIELD_SIZE || bytelen % GRPD_FIELD_SIZE)
- {
- m_RejectReason = SRT_REJ_ROGUE;
- LOGC(cnlog.Error, log << CONID() << "PEER'S GROUP wrong size: " << (bytelen/GRPD_FIELD_SIZE));
- return false;
- }
- size_t groupdata_size = bytelen / GRPD_FIELD_SIZE;
- memcpy(groupdata, begin+1, bytelen);
- if (!interpretGroup(groupdata, groupdata_size, hsreq_type_cmd) )
- {
- // m_RejectReason handled inside interpretGroup().
- return false;
- }
- have_group = true;
- HLOGC(cnlog.Debug,
- log << CONID() << "CONNECTOR'S PEER GROUP [" << groupdata[0] << "] (bytelen=" << bytelen
- << " blocklen=" << blocklen << ")");
- }
- #endif
- else if (cmd == SRT_CMD_NONE)
- {
- break;
- }
- else
- {
- // Found some block that is not interesting here. Skip this and get the next one.
- HLOGC(cnlog.Debug,
- log << CONID() << "interpretSrtHandshake: ... skipping " << MessageTypeStr(UMSG_EXT, cmd));
- }
- if (!NextExtensionBlock((begin), next, (length)))
- break;
- }
- }
- // Post-checks
- // Check if peer declared encryption
- if (!encrypted && m_config.CryptoSecret.len > 0)
- {
- if (m_config.bEnforcedEnc)
- {
- m_RejectReason = SRT_REJ_UNSECURE;
- LOGC(cnlog.Error,
- log << CONID()
- << "HS EXT: Agent declares encryption, but Peer does not - rejecting connection per "
- "enforced encryption.");
- return false;
- }
- LOGC(cnlog.Warn,
- log << CONID()
- << "HS EXT: Agent declares encryption, but Peer does not (Agent can still receive unencrypted packets "
- "from Peer).");
- // This is required so that the sender is still allowed to send data, when encryption is required,
- // just this will be for waste because the receiver won't decrypt them anyway.
- m_pCryptoControl->createFakeSndContext();
- m_pCryptoControl->m_SndKmState = SRT_KM_S_NOSECRET; // Because Peer did not send KMX, though Agent has pw
- m_pCryptoControl->m_RcvKmState = SRT_KM_S_UNSECURED; // Because Peer has no PW, as has sent no KMREQ.
- return true;
- }
- // If agent has set some nondefault congctl, then congctl is expected from the peer.
- if (agsm != "live" && !have_congctl)
- {
- m_RejectReason = SRT_REJ_CONGESTION;
- LOGC(cnlog.Error,
- log << CONID() << "HS EXT: Agent uses '" << agsm
- << "' congctl, but peer DID NOT DECLARE congctl (assuming 'live').");
- return false;
- }
- #if ENABLE_BONDING
- // m_GroupOf and locking info: NULL check won't hurt here. If the group
- // was deleted in the meantime, it will be found out later anyway and result with error.
- if (m_SrtHsSide == HSD_INITIATOR && m_parent->m_GroupOf)
- {
- // XXX Later probably needs to check if this group REQUIRES the group
- // response. Currently this implements the bonding-category group, and this
- // always requires that the listener respond with the group id, otherwise
- // it probably DID NOT UNDERSTAND THE GROUP, so the connection should be rejected.
- if (!have_group)
- {
- m_RejectReason = SRT_REJ_GROUP;
- LOGC(cnlog.Error,
- log << CONID()
- << "HS EXT: agent is a group member, but the listener did not respond with group ID. Rejecting.");
- return false;
- }
- }
- #endif
- // Ok, finished, for now.
- return true;
- }
- bool srt::CUDT::checkApplyFilterConfig(const std::string &confstr)
- {
- SrtFilterConfig cfg;
- if (!ParseFilterConfig(confstr, (cfg)))
- return false;
- // Now extract the type, if present, and
- // check if you have this type of corrector available.
- if (!PacketFilter::correctConfig(cfg))
- return false;
- string thisconf = m_config.sPacketFilterConfig.str();
- // Now parse your own string, if you have it.
- if (thisconf != "")
- {
- // - for rendezvous, both must be exactly the same (it's unspecified, which will be the first one)
- if (m_config.bRendezvous && thisconf != confstr)
- {
- return false;
- }
- SrtFilterConfig mycfg;
- if (!ParseFilterConfig(thisconf, (mycfg)))
- return false;
- // Check only if both have set a filter of the same type.
- if (mycfg.type != cfg.type)
- return false;
- // If so, then:
- // - for caller-listener configuration, accept the listener version.
- if (m_SrtHsSide == HSD_INITIATOR)
- {
- // This is a caller, this should apply all parameters received
- // from the listener, forcefully.
- for (map<string, string>::iterator x = cfg.parameters.begin(); x != cfg.parameters.end(); ++x)
- {
- mycfg.parameters[x->first] = x->second;
- }
- }
- else
- {
- if (!CheckFilterCompat((mycfg), cfg))
- return false;
- }
- HLOGC(cnlog.Debug,
- log << CONID() << "checkApplyFilterConfig: param: LOCAL: " << Printable(mycfg.parameters)
- << " FORGN: " << Printable(cfg.parameters));
- ostringstream myos;
- myos << mycfg.type;
- for (map<string, string>::iterator x = mycfg.parameters.begin(); x != mycfg.parameters.end(); ++x)
- {
- myos << "," << x->first << ":" << x->second;
- }
- m_config.sPacketFilterConfig.set(myos.str());
- HLOGC(cnlog.Debug, log << CONID() << "checkApplyFilterConfig: Effective config: " << thisconf);
- }
- else
- {
- // Take the foreign configuration as a good deal.
- HLOGC(cnlog.Debug, log << CONID() << "checkApplyFilterConfig: Good deal config: " << thisconf);
- m_config.sPacketFilterConfig.set(confstr);
- }
- size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - cfg.extra_size;
- if (m_config.zExpPayloadSize > efc_max_payload_size)
- {
- LOGC(cnlog.Warn,
- log << CONID() << "Due to filter-required extra " << cfg.extra_size << " bytes, SRTO_PAYLOADSIZE fixed to "
- << efc_max_payload_size << " bytes");
- m_config.zExpPayloadSize = efc_max_payload_size;
- }
- return true;
- }
- #if ENABLE_BONDING
- bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_ATR_UNUSED, int hsreq_type_cmd SRT_ATR_UNUSED)
- {
- // `data_size` isn't checked because we believe it's checked earlier.
- // Also this code doesn't predict to get any other format than the official one,
- // so there are only data in two fields. Passing this argument is only left
- // for consistency and possibly changes in future.
- // We are granted these two fields do exist
- SRTSOCKET grpid = groupdata[GRPD_GROUPID];
- uint32_t gd = groupdata[GRPD_GROUPDATA];
- SRT_GROUP_TYPE gtp = SRT_GROUP_TYPE(SrtHSRequest::HS_GROUP_TYPE::unwrap(gd));
- int link_weight = SrtHSRequest::HS_GROUP_WEIGHT::unwrap(gd);
- uint32_t link_flags = SrtHSRequest::HS_GROUP_FLAGS::unwrap(gd);
- if (m_config.iGroupConnect == 0)
- {
- m_RejectReason = SRT_REJ_GROUP;
- LOGC(cnlog.Error, log << CONID() << "HS/GROUP: this socket is not allowed for group connect.");
- return false;
- }
- // This is called when the group type has come in the handshake is invalid.
- if (gtp >= SRT_GTYPE_E_END)
- {
- m_RejectReason = SRT_REJ_GROUP;
- LOGC(cnlog.Error,
- log << CONID() << "HS/GROUP: incorrect group type value " << gtp << " (max is " << SRT_GTYPE_E_END << ")");
- return false;
- }
- if ((grpid & SRTGROUP_MASK) == 0)
- {
- m_RejectReason = SRT_REJ_ROGUE;
- LOGC(cnlog.Error, log << CONID() << "HS/GROUP: socket ID passed as a group ID is not a group ID");
- return false;
- }
- // We have the group, now take appropriate action.
- // The redundancy group requires to make a mirror group
- // on this side, and the newly created socket should
- // be made belong to it.
- #if ENABLE_HEAVY_LOGGING
- static const char* hs_side_name[] = {"draw", "initiator", "responder"};
- HLOGC(cnlog.Debug,
- log << CONID() << "interpretGroup: STATE: HsSide=" << hs_side_name[m_SrtHsSide]
- << " HS MSG: " << MessageTypeStr(UMSG_EXT, hsreq_type_cmd) << " $" << grpid << " type=" << gtp
- << " weight=" << link_weight << " flags=0x" << std::hex << link_flags);
- #endif
- // XXX Here are two separate possibilities:
- //
- // 1. This is a HS request and this is a newly created socket not yet part of any group.
- // 2. This is a HS response and the group is the mirror group for the group to which the agent belongs; we need to pin the mirror group as peer group
- //
- // These two situations can be only distinguished by the HS side.
- if (m_SrtHsSide == HSD_DRAW)
- {
- m_RejectReason = SRT_REJ_IPE;
- LOGC(cnlog.Error,
- log << CONID()
- << "IPE: interpretGroup: The HS side should have been already decided; it's still DRAW. Grouping "
- "rejected.");
- return false;
- }
- ScopedLock guard_group_existence (uglobal().m_GlobControlLock);
- if (m_SrtHsSide == HSD_INITIATOR)
- {
- // This is a connection initiator that has requested the peer to make a
- // mirror group and join it, then respond its mirror group id. The
- // `grpid` variable contains this group ID; map this as your peer
- // group. If your group already has a peer group set, check if this is
- // the same id, otherwise the connection should be rejected.
- // So, first check the group of the current socket and see if a peer is set.
- CUDTGroup* pg = m_parent->m_GroupOf;
- if (!pg)
- {
- // This means that the responder has responded with a group membership,
- // but the initiator did not request any group membership presence.
- // Currently impossible situation.
- m_RejectReason = SRT_REJ_IPE;
- LOGC(cnlog.Error, log << CONID() << "IPE: HS/RSP: group membership responded, while not requested.");
- return false;
- }
- // Group existence is guarded, so we can now lock the group as well.
- ScopedLock gl(*pg->exp_groupLock());
- // Now we know the group exists, but it might still be closed
- if (pg->closing())
- {
- LOGC(cnlog.Error, log << CONID() << "HS/RSP: group was closed in the process, can't continue connecting");
- m_RejectReason = SRT_REJ_IPE;
- return false;
- }
- SRTSOCKET peer = pg->peerid();
- if (peer == -1)
- {
- // This is the first connection within this group, so this group
- // has just been informed about the peer membership. Accept it.
- pg->set_peerid(grpid);
- HLOGC(cnlog.Debug,
- log << CONID() << "HS/RSP: group $" << pg->id() << " -> peer $" << pg->peerid()
- << ", copying characteristic data");
- // The call to syncWithSocket is copying
- // some interesting data from the first connected
- // socket. This should be only done for the first successful connection.
- pg->syncWithSocket(*this, HSD_INITIATOR);
- }
- // Otherwise the peer id must be the same as existing, otherwise
- // this group is considered already bound to another peer group.
- // (Note that the peer group is peer-specific, and peer id numbers
- // may repeat among sockets connected to groups established on
- // different peers).
- else if (peer != grpid)
- {
- LOGC(cnlog.Error,
- log << CONID() << "IPE: HS/RSP: group membership responded for peer $" << grpid
- << " but the current socket's group $" << pg->id() << " has already a peer $" << peer);
- m_RejectReason = SRT_REJ_GROUP;
- return false;
- }
- else
- {
- HLOGC(cnlog.Debug,
- log << CONID() << "HS/RSP: group $" << pg->id() << " ALREADY MAPPED to peer mirror $"
- << pg->peerid());
- }
- }
- else
- {
- // This is a connection responder that has been requested to make a
- // mirror group and join it. Later on, the HS response will be sent
- // and its group ID will be added to the HS extensions as mirror group
- // ID to the peer.
- SRTSOCKET lgid = makeMePeerOf(grpid, gtp, link_flags);
- if (!lgid)
- return true; // already done
- if (lgid == -1)
- {
- // NOTE: This error currently isn't reported by makeMePeerOf,
- // so this is left to handle a possible error introduced in future.
- m_RejectReason = SRT_REJ_GROUP;
- return false; // error occurred
- }
- if (!m_parent->m_GroupOf)
- {
- // Strange, we just added it...
- m_RejectReason = SRT_REJ_IPE;
- LOGC(cnlog.Fatal, log << CONID() << "IPE: socket not in group after adding to it");
- return false;
- }
- groups::SocketData* f = m_parent->m_GroupMemberData;
- f->weight = link_weight;
- f->agent = m_parent->m_SelfAddr;
- f->peer = m_PeerAddr;
- }
- m_parent->m_GroupOf->debugGroup();
- // That's all. For specific things concerning group
- // types, this will be later.
- return true;
- }
- #endif
- #if ENABLE_BONDING
- // NOTE: This function is called only in one place and it's done
- // exclusively on the listener side (HSD_RESPONDER, HSv5+).
- // [[using locked(s_UDTUnited.m_GlobControlLock)]]
- SRTSOCKET srt::CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint32_t link_flags)
- {
- // Note: This function will lock pg->m_GroupLock!
- CUDTSocket* s = m_parent;
- // Note that the socket being worked out here is about to be returned
- // from `srt_accept` call, and until this moment it will be inaccessible
- // for any other thread. It is then assumed that no other thread is accessing
- // it right now so there's no need to lock s->m_ControlLock.
- // Check if there exists a group that this one is a peer of.
- CUDTGroup* gp = uglobal().findPeerGroup_LOCKED(peergroup);
- bool was_empty = true;
- if (gp)
- {
- if (gp->type() != gtp)
- {
- LOGC(gmlog.Error,
- log << CONID() << "HS: GROUP TYPE COLLISION: peer group=$" << peergroup << " type " << gtp
- << " agent group=$" << gp->id() << " type" << gp->type());
- return -1;
- }
- HLOGC(gmlog.Debug, log << CONID() << "makeMePeerOf: group for peer=$" << peergroup << " found: $" << gp->id());
- if (!gp->groupEmpty())
- was_empty = false;
- }
- else
- {
- try
- {
- gp = &newGroup(gtp);
- }
- catch (...)
- {
- // Expected exceptions are only those referring to system resources
- return -1;
- }
- if (!gp->applyFlags(link_flags, m_SrtHsSide))
- {
- // Wrong settings. Must reject. Delete group.
- uglobal().deleteGroup_LOCKED(gp);
- return -1;
- }
- gp->set_peerid(peergroup);
- gp->deriveSettings(this);
- // This can only happen on a listener (it's only called on a site that is
- // HSD_RESPONDER), so it was a response for a groupwise connection.
- // Therefore such a group shall always be considered opened.
- gp->setOpen();
- HLOGC(gmlog.Debug,
- log << CONID() << "makeMePeerOf: no group has peer=$" << peergroup << " - creating new mirror group $"
- << gp->id());
- }
- {
- ScopedLock glock (*gp->exp_groupLock());
- if (gp->closing())
- {
- HLOGC(gmlog.Debug, log << CONID() << "makeMePeerOf: group $" << gp->id() << " is being closed, can't process");
- }
- if (was_empty)
- {
- gp->syncWithSocket(s->core(), HSD_RESPONDER);
- }
- }
- // Setting non-blocking reading for group socket.
- s->core().m_config.bSynRecving = false;
- s->core().m_config.bSynSending = false;
- // Copy of addSocketToGroup. No idea how many parts could be common, not much.
- // Check if the socket already is in the group
- groups::SocketData* f;
- if (gp->contains(m_SocketID, (f)))
- {
- // XXX This is internal error. Report it, but continue
- // (A newly created socket from acceptAndRespond should not have any group membership yet)
- LOGC(gmlog.Error, log << CONID() << "IPE (non-fatal): the socket is in the group, but has no clue about it!");
- s->m_GroupOf = gp;
- s->m_GroupMemberData = f;
- return 0;
- }
- s->m_GroupMemberData = gp->add(groups::prepareSocketData(s));
- s->m_GroupOf = gp;
- m_HSGroupType = gtp;
- // Record the remote address in the group data.
- return gp->id();
- }
- void srt::CUDT::synchronizeWithGroup(CUDTGroup* gp)
- {
- ScopedLock gl (*gp->exp_groupLock());
- // We have blocked here the process of connecting a new
- // socket and adding anything new to the group, so no such
- // thing may happen in the meantime.
- steady_clock::time_point start_time, peer_start_time;
- start_time = m_stats.tsStartTime;
- peer_start_time = m_tsRcvPeerStartTime;
- if (!gp->applyGroupTime((start_time), (peer_start_time)))
- {
- HLOGC(gmlog.Debug,
- log << CONID() << "synchronizeWithGroup: ST=" << FormatTime(m_stats.tsStartTime) << " -> "
- << FormatTime(start_time) << " PST=" << FormatTime(m_tsRcvPeerStartTime) << " -> "
- << FormatTime(peer_start_time));
- m_stats.tsStartTime = start_time;
- m_tsRcvPeerStartTime = peer_start_time;
- }
- else
- {
- // This was the first connected socket and it defined start time.
- HLOGC(gmlog.Debug,
- log << CONID() << "synchronizeWithGroup: ST=" << FormatTime(m_stats.tsStartTime)
- << " PST=" << FormatTime(m_tsRcvPeerStartTime));
- }
- steady_clock::time_point rcv_buffer_time_base;
- bool rcv_buffer_wrap_period = false;
- steady_clock::duration rcv_buffer_udrift(0);
- if (m_bTsbPd && gp->getBufferTimeBase(this, (rcv_buffer_time_base), (rcv_buffer_wrap_period), (rcv_buffer_udrift)))
- {
- // We have at least one socket in the group, each socket should have
- // the value of the timebase set exactly THE SAME.
- // In case when we have the following situation:
- // - the existing link is before [LAST30] (so wrap period is off)
- // - the new link gets the timestamp from [LAST30] range
- // --> this will be recognized as entering the wrap period, next
- // timebase will get added a segment to this value
- //
- // The only dangerous situations could be when one link gets
- // timestamps from the [FOLLOWING30] and the other in [FIRST30],
- // but between them there's a 30s distance, considered large enough
- // time to not fill a network window.
- enterCS(m_RecvLock);
- m_pRcvBuffer->applyGroupTime(rcv_buffer_time_base, rcv_buffer_wrap_period, m_iTsbPdDelay_ms * 1000, rcv_buffer_udrift);
- m_pRcvBuffer->setPeerRexmitFlag(m_bPeerRexmitFlag);
- leaveCS(m_RecvLock);
- HLOGC(gmlog.Debug, log << "AFTER HS: Set Rcv TsbPd mode: delay="
- << (m_iTsbPdDelay_ms/1000) << "." << (m_iTsbPdDelay_ms%1000)
- << "s GROUP TIME BASE: " << FormatTime(rcv_buffer_time_base)
- << " (" << (rcv_buffer_wrap_period ? "" : "NOT") << " WRAP PERIOD)");
- }
- else
- {
- HLOGC(gmlog.Debug,
- log << CONID() << "AFTER HS: (GROUP, but "
- << (m_bTsbPd ? "FIRST SOCKET is initialized normally)" : "no TSBPD set)"));
- updateSrtRcvSettings();
- }
- // This function currently does nothing, just left for consistency
- // with updateAfterSrtHandshake().
- updateSrtSndSettings();
- // These are the values that are normally set initially by setters.
- int32_t snd_isn = m_iSndLastAck, rcv_isn = m_iRcvLastAck;
- if (!gp->applyGroupSequences(m_SocketID, (snd_isn), (rcv_isn)))
- {
- HLOGC(gmlog.Debug,
- log << CONID() << "synchronizeWithGroup: DERIVED ISN: RCV=%" << m_iRcvLastAck << " -> %" << rcv_isn
- << " (shift by " << CSeqNo::seqcmp(rcv_isn, m_iRcvLastAck) << ") SND=%" << m_iSndLastAck
- << " -> %" << snd_isn << " (shift by " << CSeqNo::seqcmp(snd_isn, m_iSndLastAck) << ")");
- setInitialRcvSeq(rcv_isn);
- setInitialSndSeq(snd_isn);
- }
- else
- {
- HLOGC(gmlog.Debug,
- log << CONID() << "synchronizeWithGroup: DEFINED ISN: RCV=%" << m_iRcvLastAck << " SND=%"
- << m_iSndLastAck);
- }
- }
- #endif
- void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn)
- {
- ScopedLock cg (m_ConnectionLock);
- HLOGC(aclog.Debug, log << CONID() << "startConnect: -> " << serv_addr.str()
- << (m_config.bSynRecving ? " (SYNCHRONOUS)" : " (ASYNCHRONOUS)") << "...");
- if (!m_bOpened)
- throw CUDTException(MJ_NOTSUP, MN_NONE, 0);
- if (m_bListening)
- throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
- if (m_bConnecting || m_bConnected)
- throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0);
- // record peer/server address
- m_PeerAddr = serv_addr;
- // register this socket in the rendezvous queue
- // RendezevousQueue is used to temporarily store incoming handshake, non-rendezvous connections also require this
- // function
- steady_clock::duration ttl = m_config.tdConnTimeOut;
- if (m_config.bRendezvous)
- ttl *= 10;
- const steady_clock::time_point ttl_time = steady_clock::now() + ttl;
- m_pRcvQueue->registerConnector(m_SocketID, this, serv_addr, ttl_time);
- // The m_iType is used in the INDUCTION for nothing. This value is only regarded
- // in CONCLUSION handshake, however this must be created after the handshake version
- // is already known. UDT_DGRAM is the value that was the only valid in the old SRT
- // with HSv4 (it supported only live transmission), for HSv5 it will be changed to
- // handle handshake extension flags.
- m_ConnReq.m_iType = UDT_DGRAM;
- // Auto mode for Caller and in Rendezvous is equivalent to CIPHER_MODE_AES_CTR.
- if (m_config.iCryptoMode == CSrtConfig::CIPHER_MODE_AUTO)
- m_config.iCryptoMode = CSrtConfig::CIPHER_MODE_AES_CTR;
- // This is my current configuration
- if (m_config.bRendezvous)
- {
- // For rendezvous, use version 5 in the waveahand and the cookie.
- // In case when you get the version 4 waveahand, simply switch to
- // the legacy HSv4 rendezvous and this time send version 4 CONCLUSION.
- // The HSv4 client simply won't check the version nor the cookie and it
- // will be sending its waveahands with version 4. Only when the party
- // has sent version 5 waveahand should the agent continue with HSv5
- // rendezvous.
- m_ConnReq.m_iVersion = HS_VERSION_SRT1;
- // m_ConnReq.m_iVersion = HS_VERSION_UDT4; // <--- Change in order to do regression test.
- m_ConnReq.m_iReqType = URQ_WAVEAHAND;
- m_ConnReq.m_iCookie = bake(serv_addr);
- // This will be also passed to a HSv4 rendezvous, but fortunately the old
- // SRT didn't read this field from URQ_WAVEAHAND message, only URQ_CONCLUSION.
- m_ConnReq.m_iType = SrtHSRequest::wrapFlags(false /* no MAGIC here */, m_config.iSndCryptoKeyLen);
- IF_HEAVY_LOGGING(const bool whether = m_config.iSndCryptoKeyLen != 0);
- HLOGC(aclog.Debug,
- log << CONID() << "startConnect (rnd): " << (whether ? "" : "NOT ")
- << " Advertising PBKEYLEN - value = " << m_config.iSndCryptoKeyLen);
- m_RdvState = CHandShake::RDV_WAVING;
- m_SrtHsSide = HSD_DRAW; // initially not resolved.
- }
- else
- {
- // For caller-listener configuration, set the version 4 for INDUCTION
- // due to a serious problem in UDT code being also in the older SRT versions:
- // the listener peer simply sents the EXACT COPY of the caller's induction
- // handshake, except the cookie, which means that when the caller sents version 5,
- // the listener will respond with version 5, which is a false information. Therefore
- // HSv5 clients MUST send HS_VERSION_UDT4 from the caller, regardless of currently
- // supported handshake version.
- //
- // The HSv5 listener should only respond with INDUCTION with m_iVersion == HS_VERSION_SRT1.
- m_ConnReq.m_iVersion = HS_VERSION_UDT4;
- m_ConnReq.m_iReqType = URQ_INDUCTION;
- m_ConnReq.m_iCookie = 0;
- m_RdvState = CHandShake::RDV_INVALID;
- }
- m_ConnReq.m_iMSS = m_config.iMSS;
- // Defined as the size of the receiver buffer in packets, unless
- // SRTO_FC has been set to a less value.
- m_ConnReq.m_iFlightFlagSize = m_config.flightCapacity();
- m_ConnReq.m_iID = m_SocketID;
- CIPAddress::ntop(serv_addr, (m_ConnReq.m_piPeerIP));
- if (forced_isn == SRT_SEQNO_NONE)
- {
- forced_isn = generateISN();
- HLOGC(aclog.Debug, log << CONID() << "startConnect: ISN generated = " << forced_isn);
- }
- else
- {
- HLOGC(aclog.Debug, log << CONID() << "startConnect: ISN forced = " << forced_isn);
- }
- m_iISN = m_ConnReq.m_iISN = forced_isn;
- setInitialSndSeq(m_iISN);
- // Inform the server my configurations.
- CPacket reqpkt;
- reqpkt.setControl(UMSG_HANDSHAKE);
- reqpkt.allocate(m_iMaxSRTPayloadSize);
- // XXX NOTE: Now the memory for the payload part is allocated automatically,
- // and such allocated memory is also automatically deallocated in the
- // destructor. If you use CPacket::allocate, remember that you must not:
- // - delete this memory
- // - assign to m_pcData.
- // If you use only manual assignment to m_pCData, this is then manual
- // allocation and so it won't be deallocated in the destructor.
- //
- // (Desired would be to disallow modification of m_pcData outside the
- // control of methods.)
- // ID = 0, connection request
- reqpkt.m_iID = 0;
- size_t hs_size = m_iMaxSRTPayloadSize;
- m_ConnReq.store_to((reqpkt.m_pcData), (hs_size));
- // Note that CPacket::allocate() sets also the size
- // to the size of the allocated buffer, which not
- // necessarily is to be the size of the data.
- reqpkt.setLength(hs_size);
- const steady_clock::time_point tnow = steady_clock::now();
- m_SndLastAck2Time = tnow;
- setPacketTS(reqpkt, tnow);
- HLOGC(cnlog.Debug,
- log << CONID() << "CUDT::startConnect: REQ-TIME set HIGH (TimeStamp: " << reqpkt.m_iTimeStamp
- << "). SENDING HS: " << m_ConnReq.show());
- /*
- * Race condition if non-block connect response thread scheduled before we set m_bConnecting to true?
- * Connect response will be ignored and connecting will wait until timeout.
- * Maybe m_ConnectionLock handling problem? Not used in CUDT::connect(const CPacket& response)
- */
- m_tsLastReqTime = tnow;
- m_bConnecting = true;
- // At this point m_SourceAddr is probably default-any, but this function
- // now requires that the address be specified here because there will be
- // no possibility to do it at any next stage of sending.
- m_pSndQueue->sendto(serv_addr, reqpkt, m_SourceAddr);
- //
- ///
- //// ---> CONTINUE TO: <PEER>.CUDT::processConnectRequest()
- /// (Take the part under condition: hs.m_iReqType == URQ_INDUCTION)
- //// <--- RETURN WHEN: m_pSndQueue->sendto() is called.
- //// .... SKIP UNTIL m_pRcvQueue->recvfrom() HERE....
- //// (the first "sendto" will not be called due to being too early)
- ///
- //
- //////////////////////////////////////////////////////
- // SYNCHRO BAR
- //////////////////////////////////////////////////////
- if (!m_config.bSynRecving)
- {
- HLOGC(cnlog.Debug, log << CONID() << "startConnect: ASYNC MODE DETECTED. Deferring the process to RcvQ:worker");
- return;
- }
- // Below this bar, rest of function maintains only and exclusively
- // the SYNCHRONOUS (blocking) connection process.
- // Wait for the negotiated configurations from the peer side.
- // This packet only prepares the storage where we will read the
- // next incoming packet.
- CPacket response;
- response.setControl(UMSG_HANDSHAKE);
- response.allocate(m_iMaxSRTPayloadSize);
- CUDTException e;
- EConnectStatus cst = CONN_CONTINUE;
- // This is a temporary place to store the DESTINATION IP from the incoming packet.
- // We can't record this address yet until the cookie-confirmation is done, for safety reasons.
- sockaddr_any use_source_adr(serv_addr.family());
- while (!m_bClosing)
- {
- const steady_clock::time_point local_tnow = steady_clock::now();
- const steady_clock::duration tdiff = local_tnow - m_tsLastReqTime.load();
- // avoid sending too many requests, at most 1 request per 250ms
- // SHORT VERSION:
- // The immediate first run of this loop WILL SKIP THIS PART, so
- // the processing really begins AFTER THIS CONDITION.
- //
- // Note that some procedures inside may set m_tsLastReqTime to 0,
- // which will result of this condition to trigger immediately in
- // the next iteration.
- if (count_milliseconds(tdiff) > 250)
- {
- HLOGC(cnlog.Debug,
- log << CONID() << "startConnect: LOOP: time to send (" << count_milliseconds(tdiff)
- << " > 250 ms). size=" << reqpkt.getLength());
- if (m_config.bRendezvous)
- reqpkt.m_iID = m_ConnRes.m_iID;
- #if ENABLE_HEAVY_LOGGING
- {
- CHandShake debughs;
- debughs.load_from(reqpkt.m_pcData, reqpkt.getLength());
- HLOGC(cnlog.Debug,
- log << CONID() << "startConnect: REQ-TIME HIGH."
- << " cont/sending HS to peer: " << debughs.show());
- }
- #endif
- m_tsLastReqTime = local_tnow;
- setPacketTS(reqpkt, local_tnow);
- m_pSndQueue->sendto(serv_addr, reqpkt, use_source_adr);
- }
- else
- {
- HLOGC(cnlog.Debug,
- log << CONID() << "startConnect: LOOP: too early to send - " << count_milliseconds(tdiff)
- << " < 250ms");
- }
- cst = CONN_CONTINUE;
- response.setLength(m_iMaxSRTPayloadSize);
- if (m_pRcvQueue->recvfrom(m_SocketID, (response)) > 0)
- {
- use_source_adr = response.udpDestAddr();
- HLOGC(cnlog.Debug, log << CONID() << "startConnect: got response for connect request");
- cst = processConnectResponse(response, &e);
- HLOGC(cnlog.Debug, log << CONID() << "startConnect: response processing result: " << ConnectStatusStr(cst));
- // Expected is that:
- // - the peer responded with URQ_INDUCTION + cookie. This above function
- // should check that and craft the URQ_CONCLUSION handshake, in which
- // case this function returns CONN_CONTINUE. As an extra action taken
- // for that case, we set the SECURING mode if encryption requested,
- // and serialize again the handshake, possibly together with HS extension
- // blocks, if HSv5 peer responded. The serialized handshake will be then
- // sent again, as the loop is repeated.
- // - the peer responded with URQ_CONCLUSION. This handshake was accepted
- // as a connection, and for >= HSv5 the HS extension blocks have been
- // also read and interpreted. In this case this function returns:
- // - CONN_ACCEPT, if everything was correct - break this loop and return normally
- // - CONN_REJECT in case of any problems with the delivered handshake
- // (incorrect data or data conflict) - throw error exception
- // - the peer responded with any of URQ_ERROR_*. - throw error exception
- //
- // The error exception should make the API connect() function fail, if blocking
- // or mark the failure for that socket in epoll, if non-blocking.
- if (cst == CONN_RENDEZVOUS)
- {
- // When this function returned CONN_RENDEZVOUS, this requires
- // very special processing for the Rendezvous-v5 algorithm. This MAY
- // involve also preparing a new handshake form, also interpreting the
- // SRT handshake extension and crafting SRT handshake extension for the
- // peer, which should be next sent. When this function returns CONN_CONTINUE,
- // it means that it has done all that was required, however none of the below
- // things has to be done (this function will do it by itself if needed).
- // Otherwise the handshake rolling can be interrupted and considered complete.
- cst = processRendezvous(&response, serv_addr, RST_OK, (reqpkt));
- if (cst == CONN_CONTINUE)
- continue;
- HLOGC(cnlog.Debug,
- log << CONID() << "startConnect: processRendezvous returned cst=" << ConnectStatusStr(cst));
- if (cst == CONN_REJECT)
- {
- // Just in case it wasn't set, set this as a fallback
- if (m_RejectReason == SRT_REJ_UNKNOWN)
- m_RejectReason = SRT_REJ_ROGUE;
- // rejection or erroneous code.
- reqpkt.setLength(m_iMaxSRTPayloadSize);
- reqpkt.setControl(UMSG_HANDSHAKE);
- sendRendezvousRejection(serv_addr, (reqpkt));
- }
- }
- if (cst == CONN_REJECT)
- {
- HLOGC(cnlog.Debug,
- log << CONID() << "startConnect: REJECTED by processConnectResponse - sending SHUTDOWN");
- sendCtrl(UMSG_SHUTDOWN);
- }
- if (cst != CONN_CONTINUE && cst != CONN_CONFUSED)
- break; // --> OUTSIDE-LOOP
- // IMPORTANT
- // [[using assert(m_pCryptoControl != nullptr)]];
- // new request/response should be sent out immediately on receiving a response
- HLOGC(cnlog.Debug,
- log << CONID() << "startConnect: SYNC CONNECTION STATUS:" << ConnectStatusStr(cst)
- << ", REQ-TIME: LOW.");
- m_tsLastReqTime = steady_clock::time_point();
- // Now serialize the handshake again to the existing buffer so that it's
- // then sent later in this loop.
- // First, set the size back to the original size, m_iMaxSRTPayloadSize because
- // this is the size of the originally allocated space. It might have been
- // shrunk by serializing the INDUCTION handshake (which was required before
- // sending this packet to the output queue) and therefore be too
- // small to store the CONCLUSION handshake (with HSv5 extensions).
- reqpkt.setLength(m_iMaxSRTPayloadSize);
- HLOGC(cnlog.Debug,
- log << CONID() << "startConnect: creating HS CONCLUSION: buffer size=" << reqpkt.getLength());
- // NOTE: BUGFIX: SERIALIZE AGAIN.
- // The original UDT code didn't do it, so it was theoretically
- // turned into conclusion, but was sending still the original
- // induction handshake challenge message. It was working only
- // thanks to that simultaneously there were being sent handshake
- // messages from a separate thread (CSndQueue::worker) from
- // RendezvousQueue, this time serialized properly, which caused
- // that with blocking mode there was a kinda initial "drunk
- // passenger with taxi driver talk" until the RendezvousQueue sends
- // (when "the time comes") the right CONCLUSION handshake
- // challenge message.
- //
- // Now that this is fixed, the handshake messages from RendezvousQueue
- // are sent only when there is a rendezvous mode or non-blocking mode.
- if (!createSrtHandshake(SRT_CMD_HSREQ, SRT_CMD_KMREQ, 0, 0, (reqpkt), (m_ConnReq)))
- {
- LOGC(cnlog.Warn, log << CONID() << "createSrtHandshake failed - REJECTING.");
- cst = CONN_REJECT;
- break;
- }
- // These last 2 parameters designate the buffer, which is in use only for SRT_CMD_KMRSP.
- // If m_ConnReq.m_iVersion == HS_VERSION_UDT4, this function will do nothing,
- // except just serializing the UDT handshake.
- // The trick is that the HS challenge is with version HS_VERSION_UDT4, but the
- // listener should respond with HS_VERSION_SRT1, if it is HSv5 capable.
- }
- HLOGC(cnlog.Debug,
- log << CONID() << "startConnect: timeout from Q:recvfrom, looping again; cst=" << ConnectStatusStr(cst));
- #if ENABLE_HEAVY_LOGGING
- // Non-fatal assertion
- if (cst == CONN_REJECT) // Might be returned by processRendezvous
- {
- LOGC(cnlog.Error,
- log << CONID()
- << "startConnect: IPE: cst=REJECT NOT EXPECTED HERE, the loop should've been interrupted!");
- break;
- }
- #endif
- if (steady_clock::now() > ttl_time)
- {
- // timeout
- e = CUDTException(MJ_SETUP, MN_TIMEOUT, 0);
- m_RejectReason = SRT_REJ_TIMEOUT;
- HLOGC(cnlog.Debug,
- log << CONID() << "startConnect: TTL time " << FormatTime(ttl_time) << " exceeded, TIMEOUT.");
- break;
- }
- }
- // <--- OUTSIDE-LOOP
- // Here will fall the break when not CONN_CONTINUE.
- // CONN_RENDEZVOUS is handled by processRendezvous.
- // CONN_ACCEPT will skip this and pass on.
- if (cst == CONN_REJECT)
- {
- e = CUDTException(MJ_SETUP, MN_REJECTED, 0);
- }
- if (e.getErrorCode() == 0)
- {
- if (m_bClosing) // if the socket is closed before connection...
- e = CUDTException(MJ_SETUP, MN_CLOSED, 0);
- else if (m_ConnRes.m_iReqType > URQ_FAILURE_TYPES) // connection request rejected
- {
- m_RejectReason = RejectReasonForURQ(m_ConnRes.m_iReqType);
- e = CUDTException(MJ_SETUP, MN_REJECTED, 0);
- }
- else if ((!m_config.bRendezvous) && (m_ConnRes.m_iISN != m_iISN)) // secuity check
- e = CUDTException(MJ_SETUP, MN_SECURITY, 0);
- }
- if (e.getErrorCode() != 0)
- {
- m_bConnecting = false;
- // The process is to be abnormally terminated, remove the connector
- // now because most likely no other processing part has done anything with it.
- m_pRcvQueue->removeConnector(m_SocketID);
- throw e;
- }
- HLOGC(cnlog.Debug,
- log << CONID() << "startConnect: handshake exchange succeeded. sourceIP=" << m_SourceAddr.str());
- // Parameters at the end.
- HLOGC(cnlog.Debug,
- log << CONID() << "startConnect: END. Parameters: mss=" << m_config.iMSS
- << " max-cwnd-size=" << m_CongCtl->cgWindowMaxSize() << " cwnd-size=" << m_CongCtl->cgWindowSize()
- << " rtt=" << m_iSRTT << " bw=" << m_iBandwidth);
- }
- // Asynchronous connection
- EConnectStatus srt::CUDT::processAsyncConnectResponse(const CPacket &pkt) ATR_NOEXCEPT
- {
- EConnectStatus cst = CONN_CONTINUE;
- CUDTException e;
- ScopedLock cg(m_ConnectionLock);
- HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectResponse: got response for connect request, processing");
- cst = processConnectResponse(pkt, &e);
- HLOGC(cnlog.Debug,
- log << CONID() << "processAsyncConnectResponse: response processing result: " << ConnectStatusStr(cst)
- << "; REQ-TIME LOW to enforce immediate response");
- m_tsLastReqTime = steady_clock::time_point();
- return cst;
- }
- bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst,
- EConnectStatus cst,
- const CPacket* pResponse /*[[nullable]]*/,
- const sockaddr_any& serv_addr)
- {
- // IMPORTANT!
- // This function is called, still asynchronously, but in the order
- // of call just after the call to the above processAsyncConnectResponse.
- // This should have got the original value returned from
- // processConnectResponse through processAsyncConnectResponse.
- CPacket request;
- request.setControl(UMSG_HANDSHAKE);
- request.allocate(m_iMaxSRTPayloadSize);
- const steady_clock::time_point now = steady_clock::now();
- setPacketTS(request, now);
- HLOGC(cnlog.Debug,
- log << CONID() << "processAsyncConnectRequest: REQ-TIME: HIGH. Should prevent too quick responses.");
- m_tsLastReqTime = now;
- // ID = 0, connection request
- request.m_iID = !m_config.bRendezvous ? 0 : m_ConnRes.m_iID;
- bool status = true;
- ScopedLock cg(m_ConnectionLock);
- if (!m_bOpened) // Check the socket has not been closed before already.
- return false;
- if (cst == CONN_RENDEZVOUS)
- {
- HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: passing to processRendezvous");
- cst = processRendezvous(pResponse, serv_addr, rst, (request));
- if (cst == CONN_ACCEPT)
- {
- HLOGC(cnlog.Debug,
- log << CONID()
- << "processAsyncConnectRequest: processRendezvous completed the process and responded by itself. "
- "Done.");
- return true;
- }
- if (cst != CONN_CONTINUE)
- {
- // processRendezvous already set the reject reason
- LOGC(cnlog.Warn,
- log << CONID()
- << "processAsyncConnectRequest: REJECT reported from processRendezvous, not processing further.");
- if (m_RejectReason == SRT_REJ_UNKNOWN)
- m_RejectReason = SRT_REJ_ROGUE;
- sendRendezvousRejection(serv_addr, (request));
- status = false;
- }
- }
- else if (cst == CONN_REJECT)
- {
- // m_RejectReason already set at worker_ProcessAddressedPacket.
- LOGC(cnlog.Warn,
- log << CONID() << "processAsyncConnectRequest: REJECT reported from HS processing: "
- << srt_rejectreason_str(m_RejectReason) << " - not processing further");
- // m_tsLastReqTime = steady_clock::time_point(); XXX ?
- return false;
- }
- else
- {
- // (this procedure will be also run for HSv4 rendezvous)
- HLOGC(cnlog.Debug,
- log << CONID() << "processAsyncConnectRequest: serializing HS: buffer size=" << request.getLength());
- if (!createSrtHandshake(SRT_CMD_HSREQ, SRT_CMD_KMREQ, 0, 0, (request), (m_ConnReq)))
- {
- // All 'false' returns from here are IPE-type, mostly "invalid argument" plus "all keys expired".
- LOGC(cnlog.Error,
- log << CONID() << "IPE: processAsyncConnectRequest: createSrtHandshake failed, dismissing.");
- status = false;
- }
- else
- {
- HLOGC(cnlog.Debug,
- log << CONID()
- << "processAsyncConnectRequest: sending HS reqtype=" << RequestTypeStr(m_ConnReq.m_iReqType)
- << " to socket " << request.m_iID << " size=" << request.getLength());
- }
- }
- if (!status)
- {
- return false;
- /* XXX Shouldn't it send a single response packet for the rejection?
- // Set the version to 0 as "handshake rejection" status and serialize it
- CHandShake zhs;
- size_t size = request.getLength();
- zhs.store_to((request.m_pcData), (size));
- request.setLength(size);
- */
- }
- HLOGC(cnlog.Debug,
- log << CONID() << "processAsyncConnectRequest: setting REQ-TIME HIGH, SENDING HS:" << m_ConnReq.show());
- m_tsLastReqTime = steady_clock::now();
- m_pSndQueue->sendto(serv_addr, request, m_SourceAddr);
- return status;
- }
- void srt::CUDT::sendRendezvousRejection(const sockaddr_any& serv_addr, CPacket& r_rsppkt)
- {
- // We can reuse m_ConnReq because we are about to abandon the connection process.
- m_ConnReq.m_iReqType = URQFailure(m_RejectReason);
- // Assumed that r_rsppkt refers to a packet object that was already prepared
- // to be used for storing the handshake there.
- size_t size = r_rsppkt.getLength();
- m_ConnReq.store_to((r_rsppkt.m_pcData), (size));
- r_rsppkt.setLength(size);
- HLOGC(cnlog.Debug, log << CONID() << "sendRendezvousRejection: using code=" << m_ConnReq.m_iReqType
- << " for reject reason code " << m_RejectReason << " (" << srt_rejectreason_str(m_RejectReason) << ")");
- setPacketTS(r_rsppkt, steady_clock::now());
- m_pSndQueue->sendto(serv_addr, r_rsppkt, m_SourceAddr);
- }
- void srt::CUDT::cookieContest()
- {
- if (m_SrtHsSide != HSD_DRAW)
- return;
- LOGC(cnlog.Debug,
- log << CONID() << "cookieContest: agent=" << m_ConnReq.m_iCookie << " peer=" << m_ConnRes.m_iCookie);
- // Here m_ConnReq.m_iCookie is a local cookie value sent in connection request to the peer.
- // m_ConnRes.m_iCookie is a cookie value sent by the peer in its connection request.
- if (m_ConnReq.m_iCookie == 0 || m_ConnRes.m_iCookie == 0)
- {
- // Note that it's virtually impossible that Agent's cookie is not ready, this
- // shall be considered IPE.
- // Not all cookies are ready, don't start the contest.
- return;
- }
- // INITIATOR/RESPONDER role is resolved by COOKIE CONTEST.
- //
- // The cookie contest must be repeated every time because it
- // may change the state at some point.
- //
- // In SRT v1.4.3 and prior the below subtraction was performed in 32-bit arithmetic.
- // The result of subtraction can overflow 32-bits.
- // Example
- // m_ConnReq.m_iCookie = -1480577720;
- // m_ConnRes.m_iCookie = 811599203;
- // int64_t llBetterCookie = -1480577720 - 811599203 = -2292176923 (FFFF FFFF 7760 27E5);
- // int32_t iBetterCookie = 2002790373 (7760 27E5);
- //
- // Now 64-bit arithmetic is used to calculate the actual result of subtraction.
- // The 31-st bit is then checked to check if the resulting is negative in 32-bit aritmetics.
- // This way the old contest behavior is preserved, and potential compiler optimisations are avoided.
- const int64_t contest = int64_t(m_ConnReq.m_iCookie) - int64_t(m_ConnRes.m_iCookie);
- if ((contest & 0xFFFFFFFF) == 0)
- {
- // DRAW! The only way to continue would be to force the
- // cookies to be regenerated and to start over. But it's
- // not worth a shot - this is an extremely rare case.
- // This can simply do reject so that it can be started again.
- // Pretend then that the cookie contest wasn't done so that
- // it's done again. Cookies are baked every time anew, however
- // the successful initial contest remains valid no matter how
- // cookies will change.
- m_SrtHsSide = HSD_DRAW;
- return;
- }
- if (contest & 0x80000000)
- {
- m_SrtHsSide = HSD_RESPONDER;
- return;
- }
- m_SrtHsSide = HSD_INITIATOR;
- }
- // This function should complete the data for KMX needed for an out-of-band
- // handshake response. Possibilities are:
- // - There's no KMX (including first responder's handshake in rendezvous). This writes 0 to w_kmdatasize.
- // - The encryption status is failure. Respond with fail code and w_kmdatasize = 1.
- // - The last KMX was successful. Respond with the original kmdata and their size in w_kmdatasize.
- EConnectStatus srt::CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize)
- {
- // If the last CONCLUSION message didn't contain the KMX extension, there's
- // no key recorded yet, so it can't be extracted. Mark this w_kmdatasize empty though.
- int hs_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType);
- if (IsSet(hs_flags, CHandShake::HS_EXT_KMREQ))
- {
- // m_pCryptoControl can be NULL if the socket has been closed already. See issue #2231.
- if (!m_pCryptoControl)
- {
- m_RejectReason = SRT_REJ_IPE;
- LOGC(cnlog.Error,
- log << CONID() << "IPE: craftKmResponse needs to send KM, but CryptoControl does not exist."
- << " Socket state: connected=" << boolalpha << m_bConnected << ", connecting=" << m_bConnecting
- << ", broken=" << m_bBroken << ", opened " << m_bOpened << ", closing=" << m_bClosing << ".");
- return CONN_REJECT;
- }
- // This is a periodic handshake update, so you need to extract the KM data from the
- // first message, provided that it is there.
- size_t msgsize = m_pCryptoControl->getKmMsg_size(0);
- if (msgsize == 0)
- {
- switch (m_pCryptoControl->m_RcvKmState)
- {
- // If the KMX process ended up with a failure, the KMX is not recorded.
- // In this case as the KMRSP answer the "failure status" should be crafted.
- case SRT_KM_S_NOSECRET:
- case SRT_KM_S_BADSECRET:
- {
- HLOGC(cnlog.Debug,
- log << CONID() << "craftKmResponse: No KMX recorded, status = "
- << KmStateStr(m_pCryptoControl->m_RcvKmState) << ". Respond it.");
- // Just do the same thing as in CCryptoControl::processSrtMsg_KMREQ for that case,
- // that is, copy the NOSECRET code into KMX message.
- memcpy((aw_kmdata), &m_pCryptoControl->m_RcvKmState, sizeof(int32_t));
- w_kmdatasize = 1;
- }
- break; // Treat as ACCEPT in general; might change to REJECT on enforced-encryption
- default:
- // Remaining values:
- // UNSECURED: should not fall here at all
- // SECURING: should not happen in HSv5
- // SECURED: should have received the recorded KMX correctly (getKmMsg_size(0) > 0)
- {
- m_RejectReason = SRT_REJ_IPE;
- // Remaining situations:
- // - password only on this site: shouldn't be considered to be sent to a no-password site
- LOGC(cnlog.Error,
- log << CONID() << "craftKmResponse: IPE: PERIODIC HS: NO KMREQ RECORDED KMSTATE: RCV="
- << KmStateStr(m_pCryptoControl->m_RcvKmState)
- << " SND=" << KmStateStr(m_pCryptoControl->m_SndKmState));
- return CONN_REJECT;
- }
- break;
- }
- }
- else
- {
- w_kmdatasize = msgsize / 4;
- if (msgsize > w_kmdatasize * 4)
- {
- // Sanity check
- LOGC(cnlog.Error, log << CONID() << "IPE: KMX data not aligned to 4 bytes! size=" << msgsize);
- memset((aw_kmdata + (w_kmdatasize * 4)), 0, msgsize - (w_kmdatasize * 4));
- ++w_kmdatasize;
- }
- HLOGC(cnlog.Debug,
- log << CONID() << "craftKmResponse: getting KM DATA from the fore-recorded KMX from KMREQ, size="
- << w_kmdatasize);
- memcpy((aw_kmdata), m_pCryptoControl->getKmMsg_data(0), msgsize);
- }
- }
- else
- {
- HLOGC(cnlog.Debug, log << CONID() << "craftKmResponse: no KMX flag - not extracting KM data for KMRSP");
- w_kmdatasize = 0;
- }
- return CONN_ACCEPT;
- }
- EConnectStatus srt::CUDT::processRendezvous(
- const CPacket* pResponse /*[[nullable]]*/, const sockaddr_any& serv_addr,
- EReadStatus rst, CPacket& w_reqpkt)
- {
- if (m_RdvState == CHandShake::RDV_CONNECTED)
- {
- HLOGC(cnlog.Debug, log << CONID() << "processRendezvous: already in CONNECTED state.");
- return CONN_ACCEPT;
- }
- uint32_t kmdata[SRTDATA_MAXSIZE];
- size_t kmdatasize = SRTDATA_MAXSIZE;
- cookieContest();
- // We know that the other side was contacted and the other side has sent
- // the handshake message - we know then both cookies. If it's a draw, it's
- // a very rare case of creating identical cookies.
- if (m_SrtHsSide == HSD_DRAW)
- {
- m_RejectReason = SRT_REJ_RDVCOOKIE;
- LOGC(cnlog.Error,
- log << CONID() << "COOKIE CONTEST UNRESOLVED: can't assign connection roles, please wait another minute.");
- return CONN_REJECT;
- }
- UDTRequestType rsp_type = URQ_FAILURE_TYPES; // just to track uninitialized errors
- // We can assume that the Handshake packet received here as 'response'
- // is already serialized in m_ConnRes. Check extra flags that are meaningful
- // for further processing here.
- int ext_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType);
- bool needs_extension = ext_flags != 0; // Initial value: received HS has extensions.
- bool needs_hsrsp;
- rendezvousSwitchState((rsp_type), (needs_extension), (needs_hsrsp));
- if (rsp_type > URQ_FAILURE_TYPES)
- {
- m_RejectReason = RejectReasonForURQ(rsp_type);
- HLOGC(cnlog.Debug,
- log << CONID()
- << "processRendezvous: rejecting due to switch-state response: " << RequestTypeStr(rsp_type));
- return CONN_REJECT;
- }
- checkUpdateCryptoKeyLen("processRendezvous", m_ConnRes.m_iType);
- // We have three possibilities here as it comes to HSREQ extensions:
- // 1. The agent is loser in attention state, it sends EMPTY conclusion (without extensions)
- // 2. The agent is loser in initiated state, it interprets incoming HSREQ and creates HSRSP
- // 3. The agent is winner in attention or fine state, it sends HSREQ extension
- m_ConnReq.m_iReqType = rsp_type;
- m_ConnReq.m_extension = needs_extension;
- // This must be done before prepareConnectionObjects(), because it sets ISN and m_iMaxSRTPayloadSize needed to create buffers.
- if (!applyResponseSettings(pResponse))
- {
- LOGC(cnlog.Error, log << CONID() << "processRendezvous: rogue peer");
- return CONN_REJECT;
- }
- // The CryptoControl must be created by the prepareConnectionObjects() before interpreting and creating HSv5 extensions
- // because the it will be used there.
- if (!prepareConnectionObjects(m_ConnRes, m_SrtHsSide, NULL) || !prepareBuffers(NULL))
- {
- // m_RejectReason already handled
- HLOGC(cnlog.Debug,
- log << CONID() << "processRendezvous: rejecting due to problems in prepareConnectionObjects.");
- return CONN_REJECT;
- }
- // Case 2.
- if (needs_hsrsp)
- {
- // This means that we have received HSREQ extension with the handshake, so we need to interpret
- // it and craft the response.
- if (rst == RST_OK)
- {
- // We have JUST RECEIVED packet in this session (not that this is called as periodic update).
- // Sanity check
- m_tsLastReqTime = steady_clock::time_point();
- if (!pResponse || pResponse->getLength() == size_t(-1))
- {
- m_RejectReason = SRT_REJ_IPE;
- LOGC(cnlog.Fatal,
- log << CONID() << "IPE: rst=RST_OK, but the packet has set -1 length - REJECTING (REQ-TIME: LOW)");
- return CONN_REJECT;
- }
- if (!interpretSrtHandshake(m_ConnRes, *pResponse, kmdata, &kmdatasize))
- {
- HLOGC(cnlog.Debug,
- log << CONID() << "processRendezvous: rejecting due to problems in interpretSrtHandshake REQ-TIME: LOW.");
- return CONN_REJECT;
- }
- updateAfterSrtHandshake(HS_VERSION_SRT1);
- // Pass on, inform about the shortened response-waiting period.
- HLOGC(cnlog.Debug, log << CONID() << "processRendezvous: setting REQ-TIME: LOW. Forced to respond immediately.");
- }
- else
- {
- // This is a repeated handshake, so you can't use the incoming data to
- // prepare data for createSrtHandshake. They have to be extracted from inside.
- EConnectStatus conn = craftKmResponse((kmdata), (kmdatasize));
- if (conn != CONN_ACCEPT)
- return conn;
- }
- // No matter the value of needs_extension, the extension is always needed
- // when HSREQ was interpreted (to store HSRSP extension).
- m_ConnReq.m_extension = true;
- HLOGC(cnlog.Debug,
- log << CONID()
- << "processRendezvous: HSREQ extension ok, creating HSRSP response. kmdatasize=" << kmdatasize);
- w_reqpkt.setLength(m_iMaxSRTPayloadSize);
- if (!createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP,
- kmdata, kmdatasize,
- (w_reqpkt), (m_ConnReq)))
- {
- HLOGC(cnlog.Debug,
- log << CONID()
- << "processRendezvous: rejecting due to problems in createSrtHandshake. REQ-TIME: LOW");
- m_tsLastReqTime = steady_clock::time_point();
- return CONN_REJECT;
- }
- // This means that it has received URQ_CONCLUSION with HSREQ, agent is then in RDV_FINE
- // state, it sends here URQ_CONCLUSION with HSREQ/KMREQ extensions and it awaits URQ_AGREEMENT.
- return CONN_CONTINUE;
- }
- // Special case: if URQ_AGREEMENT is to be sent, when this side is INITIATOR,
- // then it must have received HSRSP, so it must interpret it. Otherwise it would
- // end up with URQ_DONE, which means that it is the other side to interpret HSRSP.
- if (m_SrtHsSide == HSD_INITIATOR && m_ConnReq.m_iReqType == URQ_AGREEMENT)
- {
- // The same is done in CUDT::postConnect(), however this section will
- // not be done in case of rendezvous. The section in postConnect() is
- // predicted to run only in regular CALLER handling.
- if (rst != RST_OK || !pResponse || pResponse->getLength() == size_t(-1))
- {
- // Actually the -1 length would be an IPE, but it's likely that this was reported already.
- HLOGC(
- cnlog.Debug,
- log << CONID()
- << "processRendezvous: no INCOMING packet, NOT interpreting extensions (relying on exising data)");
- }
- else
- {
- HLOGC(cnlog.Debug,
- log << CONID() << "processRendezvous: INITIATOR, will send AGREEMENT - interpreting HSRSP extension");
- if (!interpretSrtHandshake(m_ConnRes, *pResponse, 0, 0))
- {
- // m_RejectReason is already set, so set the reqtype accordingly
- m_ConnReq.m_iReqType = URQFailure(m_RejectReason);
- return CONN_REJECT;
- }
- }
- // This should be false, make a kinda assert here.
- if (needs_extension)
- {
- LOGC(cnlog.Fatal,
- log << CONID() << "IPE: INITIATOR responding AGREEMENT should declare no extensions to HS");
- m_ConnReq.m_extension = false;
- }
- updateAfterSrtHandshake(HS_VERSION_SRT1);
- }
- HLOGC(cnlog.Debug,
- log << CONID() << "processRendezvous: COOKIES Agent/Peer: " << m_ConnReq.m_iCookie << "/"
- << m_ConnRes.m_iCookie << " HSD:" << (m_SrtHsSide == HSD_INITIATOR ? "initiator" : "responder")
- << " STATE:" << CHandShake::RdvStateStr(m_RdvState) << " ...");
- if (rsp_type == URQ_DONE)
- {
- HLOGC(cnlog.Debug, log << CONID() << "... WON'T SEND any response, both sides considered connected");
- }
- else
- {
- HLOGC(cnlog.Debug,
- log << CONID() << "... WILL SEND " << RequestTypeStr(rsp_type) << " "
- << (m_ConnReq.m_extension ? "with" : "without") << " SRT HS extensions");
- }
- // This marks the information for the serializer that
- // the SRT handshake extension is required.
- // Rest of the data will be filled together with
- // serialization.
- m_ConnReq.m_extension = needs_extension;
- w_reqpkt.setLength(m_iMaxSRTPayloadSize);
- if (m_RdvState == CHandShake::RDV_CONNECTED)
- {
- int cst = postConnect(pResponse, true, 0);
- if (cst == CONN_REJECT)
- {
- // m_RejectReason already set
- HLOGC(cnlog.Debug, log << CONID() << "processRendezvous: rejecting due to problems in postConnect.");
- return CONN_REJECT;
- }
- }
- // URQ_DONE or URQ_AGREEMENT can be the result if the state is RDV_CONNECTED.
- // If URQ_DONE, then there's nothing to be done, when URQ_AGREEMENT then return
- // CONN_CONTINUE to make the caller send again the contents if the packet buffer,
- // this time with URQ_AGREEMENT message, but still consider yourself connected.
- if (rsp_type == URQ_DONE)
- {
- HLOGC(cnlog.Debug, log << CONID() << "processRendezvous: rsp=DONE, reporting ACCEPT (nothing to respond)");
- return CONN_ACCEPT;
- }
- // createSrtHandshake moved here because if the above conditions are satisfied,
- // no response is going to be send, so nothing needs to be "created".
- // needs_extension here distinguishes between cases 1 and 3.
- // NOTE: in case when interpretSrtHandshake was run under the conditions above (to interpret HSRSP),
- // then createSrtHandshake below will create only empty AGREEMENT message.
- if (!createSrtHandshake(SRT_CMD_HSREQ, SRT_CMD_KMREQ, 0, 0,
- (w_reqpkt), (m_ConnReq)))
- {
- // m_RejectReason already set
- LOGC(cnlog.Warn, log << CONID() << "createSrtHandshake failed (IPE?), connection rejected. REQ-TIME: LOW");
- m_tsLastReqTime = steady_clock::time_point();
- return CONN_REJECT;
- }
- if (rsp_type == URQ_AGREEMENT && m_RdvState == CHandShake::RDV_CONNECTED)
- {
- // We are using our own serialization method (not the one called after
- // processConnectResponse, this is skipped in case when this function
- // is called), so we can also send this immediately. Agreement must be
- // sent just once and the party must switch into CONNECTED state - in
- // contrast to CONCLUSION messages, which should be sent in loop repeatedly.
- //
- // Even though in theory the AGREEMENT message sent just once may miss
- // the target (as normal thing in UDP), this is little probable to happen,
- // and this doesn't matter much because even if the other party doesn't
- // get AGREEMENT, but will get payload or KEEPALIVE messages, it will
- // turn into connected state as well. The AGREEMENT is rather kinda
- // catalyzer here and may turn the entity on the right track faster. When
- // AGREEMENT is missed, it may have kinda initial tearing.
- const steady_clock::time_point now = steady_clock::now();
- m_tsLastReqTime = now;
- setPacketTS(w_reqpkt, now);
- HLOGC(cnlog.Debug,
- log << CONID()
- << "processRendezvous: rsp=AGREEMENT, reporting ACCEPT and sending just this one, REQ-TIME HIGH.");
- m_pSndQueue->sendto(serv_addr, w_reqpkt, m_SourceAddr);
- return CONN_ACCEPT;
- }
- if (rst == RST_OK)
- {
- // the request time must be updated so that the next handshake can be sent out immediately
- HLOGC(cnlog.Debug,
- log << "processRendezvous: rsp=" << RequestTypeStr(m_ConnReq.m_iReqType)
- << " REQ-TIME: LOW to send immediately, consider yourself conencted");
- m_tsLastReqTime = steady_clock::time_point();
- }
- else
- {
- HLOGC(cnlog.Debug,
- log << CONID() << "processRendezvous: REQ-TIME: remains previous value, consider yourself connected");
- }
- return CONN_CONTINUE;
- }
- // [[using locked(m_ConnectionLock)]];
- EConnectStatus srt::CUDT::processConnectResponse(const CPacket& response, CUDTException* eout) ATR_NOEXCEPT
- {
- // NOTE: ASSUMED LOCK ON: m_ConnectionLock.
- // this is the 2nd half of a connection request. If the connection is setup successfully this returns 0.
- // Returned values:
- // - CONN_REJECT: there was some error when processing the response, connection should be rejected
- // - CONN_ACCEPT: the handshake is done and finished correctly
- // - CONN_CONTINUE: the induction handshake has been processed correctly, and expects CONCLUSION handshake
- if (!m_bConnecting)
- return CONN_REJECT;
- // This is required in HSv5 rendezvous, in which it should send the URQ_AGREEMENT message to
- // the peer, however switch to connected state.
- HLOGC(cnlog.Debug,
- log << CONID() << "processConnectResponse: TYPE:"
- << (response.isControl() ? MessageTypeStr(response.getType(), response.getExtendedType())
- : string("DATA")));
- // ConnectStatus res = CONN_REJECT; // used later for status - must be declared here due to goto POST_CONNECT.
- // For HSv4, the data sender is INITIATOR, and the data receiver is RESPONDER,
- // regardless of the connecting side affiliation. This will be changed for HSv5.
- bool bidirectional = false;
- HandshakeSide hsd = m_config.bDataSender ? HSD_INITIATOR : HSD_RESPONDER;
- // (defined here due to 'goto' below).
- // SRT peer may send the SRT handshake private message (type 0x7fff) before a keep-alive.
- // This condition is checked when the current agent is trying to do connect() in rendezvous mode,
- // but the peer was faster to send a handshake packet earlier. This makes it continue with connecting
- // process if the peer is already behaving as if the connection was already established.
- // This value will check either the initial value, which is less than SRT1, or
- // the value previously loaded to m_ConnReq during the previous handshake response.
- // For the initial form this value should not be checked.
- bool hsv5 = m_ConnRes.m_iVersion >= HS_VERSION_SRT1;
- if (m_config.bRendezvous &&
- (m_RdvState == CHandShake::RDV_CONNECTED // somehow Rendezvous-v5 switched it to CONNECTED.
- || !response.isControl() // WAS A PAYLOAD PACKET.
- || (response.getType() == UMSG_KEEPALIVE) // OR WAS A UMSG_KEEPALIVE message.
- || (response.getType() == UMSG_EXT) // OR WAS a CONTROL packet of some extended type (i.e. any SRT specific)
- )
- // This may happen if this is an initial state in which the socket type was not yet set.
- // If this is a field that holds the response handshake record from the peer, this means that it wasn't received
- // yet. HSv5: added version check because in HSv5 the m_iType field has different meaning and it may be 0 in
- // case when the handshake does not carry SRT extensions.
- && (hsv5 || m_ConnRes.m_iType != UDT_UNDEFINED))
- {
- // a data packet or a keep-alive packet comes, which means the peer side is already connected
- // in this situation, the previously recorded response will be used
- // In HSv5 this situation is theoretically possible if this party has missed the URQ_AGREEMENT message.
- HLOGC(cnlog.Debug, log << CONID() << "processConnectResponse: already connected - pinning in");
- if (hsv5)
- {
- m_RdvState = CHandShake::RDV_CONNECTED;
- }
- return postConnect(&response, hsv5, eout);
- }
- if (!response.isControl(UMSG_HANDSHAKE))
- {
- m_RejectReason = SRT_REJ_ROGUE;
- if (!response.isControl())
- {
- LOGC(cnlog.Warn, log << CONID() << "processConnectResponse: received DATA while HANDSHAKE expected");
- }
- else
- {
- LOGC(cnlog.Error,
- log << CONID()
- << "processConnectResponse: CONFUSED: expected UMSG_HANDSHAKE as connection not yet established, "
- "got: "
- << MessageTypeStr(response.getType(), response.getExtendedType()));
- if (response.getType() == UMSG_SHUTDOWN)
- {
- LOGC(cnlog.Error,
- log << CONID() << "processConnectResponse: UMSG_SHUTDOWN received, rejecting connection.");
- return CONN_REJECT;
- }
- }
- if (m_config.bRendezvous)
- {
- // In rendezvous mode we expect that both sides are known
- // to the service operator (unlike a listener, which may
- // operate connections from unknown sources). This means that
- // the connection process should be terminated anyway, on
- // whichever side it would happen.
- return CONN_REJECT;
- }
- return CONN_CONFUSED;
- }
- if (m_config.bRendezvous)
- {
- m_SourceAddr = response.udpDestAddr();
- }
- if (m_ConnRes.load_from(response.m_pcData, response.getLength()) == -1)
- {
- m_RejectReason = SRT_REJ_ROGUE;
- // Handshake data were too small to reach the Handshake structure. Reject.
- LOGC(cnlog.Error,
- log << CONID()
- << "processConnectResponse: HANDSHAKE data buffer too small - possible blueboxing. Rejecting.");
- return CONN_REJECT;
- }
- HLOGC(cnlog.Debug, log << CONID() << "processConnectResponse: HS RECEIVED: " << m_ConnRes.show());
- if (m_ConnRes.m_iReqType >= URQ_FAILURE_TYPES)
- {
- m_RejectReason = RejectReasonForURQ(m_ConnRes.m_iReqType);
- LOGC(cnlog.Warn,
- log << CONID() << "processConnectResponse: rejecting per reception of a rejection HS response: "
- << RequestTypeStr(m_ConnRes.m_iReqType));
- return CONN_REJECT;
- }
- if (size_t(m_ConnRes.m_iMSS) > CPacket::ETH_MAX_MTU_SIZE)
- {
- // Yes, we do abort to prevent buffer overrun. Set your MSS correctly
- // and you'll avoid problems.
- m_RejectReason = SRT_REJ_ROGUE;
- LOGC(cnlog.Fatal, log << CONID() << "MSS size " << m_config.iMSS << "exceeds MTU size!");
- return CONN_REJECT;
- }
- // (see createCrypter() call below)
- //
- // The CCryptoControl attached object must be created early
- // because it will be required to create a conclusion handshake in HSv5
- //
- if (m_config.bRendezvous)
- {
- // SANITY CHECK: A rendezvous socket should reject any caller requests (it's not a listener)
- if (m_ConnRes.m_iReqType == URQ_INDUCTION)
- {
- m_RejectReason = SRT_REJ_ROGUE;
- LOGC(cnlog.Error,
- log << CONID()
- << "processConnectResponse: Rendezvous-point received INDUCTION handshake (expected WAVEAHAND). "
- "Rejecting.");
- return CONN_REJECT;
- }
- // The procedure for version 5 is completely different and changes the states
- // differently, so the old code will still maintain HSv4 the old way.
- if (m_ConnRes.m_iVersion > HS_VERSION_UDT4)
- {
- HLOGC(cnlog.Debug, log << CONID() << "processConnectResponse: Rendezvous HSv5 DETECTED.");
- return CONN_RENDEZVOUS; // --> will continue in CUDT::processRendezvous().
- }
- HLOGC(cnlog.Debug, log << CONID() << "processConnectResponse: Rendsezvous HSv4 DETECTED.");
- // So, here it has either received URQ_WAVEAHAND handshake message (while it should be in URQ_WAVEAHAND itself)
- // or it has received URQ_CONCLUSION/URQ_AGREEMENT message while this box has already sent URQ_WAVEAHAND to the
- // peer, and DID NOT send the URQ_CONCLUSION yet.
- if (m_ConnReq.m_iReqType == URQ_WAVEAHAND || m_ConnRes.m_iReqType == URQ_WAVEAHAND)
- {
- HLOGC(cnlog.Debug,
- log << CONID() << "processConnectResponse: REQ-TIME LOW. got HS RDV. Agent state:"
- << RequestTypeStr(m_ConnReq.m_iReqType) << " Peer HS:" << m_ConnRes.show());
- // Here we could have received WAVEAHAND or CONCLUSION.
- // For HSv4 simply switch to CONCLUSION for the sake of further handshake rolling.
- // For HSv5, make the cookie contest and basing on this decide, which party
- // should provide the HSREQ/KMREQ attachment.
- if (!createCrypter(hsd, false /* unidirectional */))
- {
- m_RejectReason = SRT_REJ_RESOURCE;
- m_ConnReq.m_iReqType = URQFailure(SRT_REJ_RESOURCE);
- // the request time must be updated so that the next handshake can be sent out immediately.
- m_tsLastReqTime = steady_clock::time_point();
- return CONN_REJECT;
- }
- m_ConnReq.m_iReqType = URQ_CONCLUSION;
- // the request time must be updated so that the next handshake can be sent out immediately.
- m_tsLastReqTime = steady_clock::time_point();
- return CONN_CONTINUE;
- }
- else
- {
- HLOGC(cnlog.Debug, log << CONID() << "processConnectResponse: Rendezvous HSv4 PAST waveahand");
- }
- }
- else
- {
- // set cookie
- if (m_ConnRes.m_iReqType == URQ_INDUCTION)
- {
- HLOGC(cnlog.Debug,
- log << CONID() << "processConnectResponse: REQ-TIME LOW; got INDUCTION HS response (cookie:" << hex
- << m_ConnRes.m_iCookie << " version:" << dec << m_ConnRes.m_iVersion
- << "), sending CONCLUSION HS with this cookie");
- m_ConnReq.m_iCookie = m_ConnRes.m_iCookie;
- m_ConnReq.m_iReqType = URQ_CONCLUSION;
- // Here test if the LISTENER has responded with version HS_VERSION_SRT1,
- // it means that it is HSv5 capable. It can still accept the HSv4 handshake.
- if (m_ConnRes.m_iVersion > HS_VERSION_UDT4)
- {
- const int hs_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType);
- if (hs_flags != SrtHSRequest::SRT_MAGIC_CODE)
- {
- LOGC(cnlog.Warn,
- log << CONID() << "processConnectResponse: Listener HSv5 did not set the SRT_MAGIC_CODE.");
- m_RejectReason = SRT_REJ_ROGUE;
- return CONN_REJECT;
- }
- checkUpdateCryptoKeyLen("processConnectResponse", m_ConnRes.m_iType);
- // This will catch HS_VERSION_SRT1 and any newer.
- // Set your highest version.
- m_ConnReq.m_iVersion = HS_VERSION_SRT1;
- // CONTROVERSIAL: use 0 as m_iType according to the meaning in HSv5.
- // The HSv4 client might not understand it, which means that agent
- // must switch itself to HSv4 rendezvous, and this time iType should
- // be set to UDT_DGRAM value.
- m_ConnReq.m_iType = 0;
- // This marks the information for the serializer that
- // the SRT handshake extension is required.
- // Rest of the data will be filled together with
- // serialization.
- m_ConnReq.m_extension = true;
- // For HSv5, the caller is INITIATOR and the listener is RESPONDER.
- // The m_config.bDataSender value should be completely ignored and the
- // connection is always bidirectional.
- bidirectional = true;
- hsd = HSD_INITIATOR;
- m_SrtHsSide = hsd;
- }
- m_tsLastReqTime = steady_clock::time_point();
- if (!createCrypter(hsd, bidirectional))
- {
- m_RejectReason = SRT_REJ_RESOURCE;
- return CONN_REJECT;
- }
- // NOTE: This setup sets URQ_CONCLUSION and appropriate data in the handshake structure.
- // The full handshake to be sent will be filled back in the caller function -- CUDT::startConnect().
- return CONN_CONTINUE;
- }
- }
- return postConnect(&response, false, eout);
- }
- bool srt::CUDT::applyResponseSettings(const CPacket* pHspkt /*[[nullable]]*/) ATR_NOEXCEPT
- {
- if (!m_ConnRes.valid())
- {
- LOGC(cnlog.Error, log << CONID() << "applyResponseSettings: ROGUE HANDSHAKE - rejecting");
- m_RejectReason = SRT_REJ_ROGUE;
- return false;
- }
- // Re-configure according to the negotiated values.
- m_config.iMSS = m_ConnRes.m_iMSS;
- m_iFlowWindowSize = m_ConnRes.m_iFlightFlagSize;
- const int udpsize = m_config.iMSS - CPacket::UDP_HDR_SIZE;
- m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE;
- m_iPeerISN = m_ConnRes.m_iISN;
- setInitialRcvSeq(m_iPeerISN);
- m_iRcvCurrPhySeqNo = CSeqNo::decseq(m_ConnRes.m_iISN);
- m_PeerID = m_ConnRes.m_iID;
- memcpy((m_piSelfIP), m_ConnRes.m_piPeerIP, sizeof m_piSelfIP);
- if (pHspkt)
- m_SourceAddr = pHspkt->udpDestAddr();
- HLOGC(cnlog.Debug,
- log << CONID() << "applyResponseSettings: HANSHAKE CONCLUDED. SETTING: payload-size=" << m_iMaxSRTPayloadSize
- << " mss=" << m_ConnRes.m_iMSS << " flw=" << m_ConnRes.m_iFlightFlagSize << " peer-ISN=" << m_ConnRes.m_iISN
- << " local-ISN=" << m_iISN
- << " peerID=" << m_ConnRes.m_iID
- << " sourceIP=" << m_SourceAddr.str());
- return true;
- }
- EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, CUDTException *eout) ATR_NOEXCEPT
- {
- if (m_ConnRes.m_iVersion < HS_VERSION_SRT1)
- m_tsRcvPeerStartTime = steady_clock::time_point(); // will be set correctly in SRT HS.
- // This procedure isn't being executed in rendezvous because
- // in rendezvous it's completed before calling this function.
- if (!rendezvous)
- {
- HLOGC(cnlog.Debug, log << CONID() << boolalpha << "postConnect: packet:" << bool(pResponse) << " rendezvous:" << rendezvous);
- // The "local storage depleted" case shouldn't happen here, but
- // this is a theoretical path that needs prevention.
- bool ok = pResponse;
- if (!ok)
- {
- m_RejectReason = SRT_REJ_IPE;
- if (eout)
- {
- *eout = CUDTException(MJ_SETUP, MN_REJECTED, 0);
- }
- return CONN_REJECT;
- }
- // [[assert (pResponse != NULL)]];
- // NOTE: THIS function must be called before calling prepareConnectionObjects.
- // The reason why it's not part of prepareConnectionObjects is that the activities
- // done there are done SIMILAR way in acceptAndRespond, which also calls this
- // function. In fact, prepareConnectionObjects() represents the code that was
- // done separately in processConnectResponse() and acceptAndRespond(), so this way
- // this code is now common. Now acceptAndRespond() does "manually" something similar
- // to applyResponseSettings(), just a little bit differently. This SHOULD be made
- // common as a part of refactoring job, just needs a bit more time.
- //
- // Currently just this function must be called always BEFORE prepareConnectionObjects
- // everywhere except acceptAndRespond().
- ok = applyResponseSettings(pResponse);
- // This will actually be done also in rendezvous HSv4,
- // however in this case the HSREQ extension will not be attached,
- // so it will simply go the "old way".
- // (&&: skip if failed already)
- // Must be called before interpretSrtHandshake() to create the CryptoControl.
- ok = ok && prepareConnectionObjects(m_ConnRes, m_SrtHsSide, eout);
- // May happen that 'response' contains a data packet that was sent in rendezvous mode.
- // In this situation the interpretation of handshake was already done earlier.
- ok = ok && pResponse->isControl();
- ok = ok && interpretSrtHandshake(m_ConnRes, *pResponse, 0, 0);
- ok = ok && prepareBuffers(eout);
- if (!ok)
- {
- if (eout)
- {
- *eout = CUDTException(MJ_SETUP, MN_REJECTED, 0);
- }
- // m_RejectReason already set
- return CONN_REJECT;
- }
- }
- bool have_group = false;
- {
- #if ENABLE_BONDING
- ScopedLock cl (uglobal().m_GlobControlLock);
- CUDTGroup* g = m_parent->m_GroupOf;
- if (g)
- {
- // This is the last moment when this can be done.
- // The updateAfterSrtHandshake call will copy the receiver
- // start time to the receiver buffer data, so the correct
- // value must be set before this happens.
- synchronizeWithGroup(g);
- have_group = true;
- }
- #endif
- }
- if (!have_group)
- {
- // This function will be called internally inside
- // synchronizeWithGroup(). This is just more complicated.
- updateAfterSrtHandshake(m_ConnRes.m_iVersion);
- }
- CInfoBlock ib;
- ib.m_iIPversion = m_PeerAddr.family();
- CInfoBlock::convert(m_PeerAddr, ib.m_piIP);
- if (m_pCache->lookup(&ib) >= 0)
- {
- m_iSRTT = ib.m_iSRTT;
- m_iRTTVar = ib.m_iSRTT / 2;
- m_iBandwidth = ib.m_iBandwidth;
- }
- #if SRT_DEBUG_RTT
- s_rtt_trace.trace(steady_clock::now(), "Connect", -1, -1,
- m_bIsFirstRTTReceived, -1, m_iSRTT, m_iRTTVar);
- #endif
- SRT_REJECT_REASON rr = setupCC();
- if (rr != SRT_REJ_UNKNOWN)
- {
- m_RejectReason = rr;
- return CONN_REJECT;
- }
- // And, I am connected too.
- m_bConnecting = false;
- // The lock on m_ConnectionLock should still be applied, but
- // the socket could have been started removal before this function
- // has started. Do a sanity check before you continue with the
- // connection process.
- CUDTSocket* s = uglobal().locateSocket(m_SocketID);
- if (s)
- {
- // The socket could be closed at this very moment.
- // Continue with removing the socket from the pending structures,
- // but prevent it from setting it as connected.
- m_bConnected = true;
- // register this socket for receiving data packets
- m_pRNode->m_bOnList = true;
- m_pRcvQueue->setNewEntry(this);
- }
- // XXX Problem around CONN_CONFUSED!
- // If some too-eager packets were received from a listener
- // that thinks it's connected, but his last handshake was missed,
- // they are collected by CRcvQueue::storePktClone. The removeConnector
- // function will want to delete them all, so it would be nice
- // if these packets can be re-delivered. Of course the listener
- // should be prepared to resend them (as every packet can be lost
- // on UDP), but it's kinda overkill when we have them already and
- // can dispatch them.
- // Remove from rendezvous queue (in this particular case it's
- // actually removing the socket that undergoes asynchronous HS processing).
- // Removing at THIS point because since when setNewEntry is called,
- // the next iteration in the CRcvQueue::worker loop will be dispatching
- // packets normally, as within-connection, so the "connector" won't
- // play any role since this time.
- // The connector, however, must stay alive until the setNewEntry is called
- // because otherwise the packets that are coming for this socket before the
- // connection process is complete will be rejected as "attack", instead of
- // being enqueued for later pickup from the queue.
- m_pRcvQueue->removeConnector(m_SocketID);
- // Ok, no more things to be done as per "clear connecting state"
- if (!s)
- {
- LOGC(cnlog.Error, log << CONID() << "Connection broken in the process - socket closed");
- m_RejectReason = SRT_REJ_CLOSE;
- if (eout)
- {
- *eout = CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
- }
- return CONN_REJECT;
- }
- // copy address information of local node
- // the local port must be correctly assigned BEFORE CUDT::startConnect(),
- // otherwise if startConnect() fails, the multiplexer cannot be located
- // by garbage collection and will cause leak
- s->core().m_pSndQueue->m_pChannel->getSockAddr((s->m_SelfAddr));
- CIPAddress::pton((s->m_SelfAddr), s->core().m_piSelfIP, m_PeerAddr);
- //int token = -1;
- #if ENABLE_BONDING
- {
- ScopedLock cl (uglobal().m_GlobControlLock);
- CUDTGroup* g = m_parent->m_GroupOf;
- if (g)
- {
- // XXX this might require another check of group type.
- // For redundancy group, at least, update the status in the group.
- // LEAVING as comment for historical reasons. Locking is here most
- // likely not necessary because the socket cannot be removed from the
- // group until the socket isn't removed, and this requires locking of
- // m_GlobControlLock. This should ensure that when m_GroupOf is
- // not NULL, m_GroupMemberData is also valid.
- // ScopedLock glock(g->m_GroupLock);
- HLOGC(cnlog.Debug, log << "group: Socket @" << m_parent->m_SocketID << " fresh connected, setting IDLE");
- groups::SocketData* gi = m_parent->m_GroupMemberData;
- gi->sndstate = SRT_GST_IDLE;
- gi->rcvstate = SRT_GST_IDLE;
- gi->laststatus = SRTS_CONNECTED;
- //token = gi->token;
- g->setGroupConnected();
- }
- }
- #endif
- s->m_Status = SRTS_CONNECTED;
- // acknowledde any waiting epolls to write
- uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_CONNECT, true);
- CGlobEvent::triggerEvent();
- /* XXX Likely it should NOT be called here for two reasons:
- - likely lots of mutexes are locked here so any
- API call from here might cause a deadlock
- - if called from an asynchronous connection process, it was
- already called from inside updateConnStatus
- - if called from startConnect (synchronous mode), it is even wrong.
- if (m_cbConnectHook)
- {
- CALLBACK_CALL(m_cbConnectHook, m_SocketID, SRT_SUCCESS, m_PeerAddr.get(), token);
- }
- */
- LOGC(cnlog.Note, log << CONID() << "Connection established to: " << m_PeerAddr.str());
- return CONN_ACCEPT;
- }
- void srt::CUDT::checkUpdateCryptoKeyLen(const char *loghdr SRT_ATR_UNUSED, int32_t typefield)
- {
- int enc_flags = SrtHSRequest::SRT_HSTYPE_ENCFLAGS::unwrap(typefield);
- // potentially 0-7 values are possible.
- // When 0, don't change anything - it should rely on the value 0.
- // When 1, 5, 6, 7, this is kinda internal error - ignore.
- if (enc_flags >= 2 && enc_flags <= 4) // 2 = 128, 3 = 192, 4 = 256
- {
- int rcv_pbkeylen = SrtHSRequest::SRT_PBKEYLEN_BITS::wrap(enc_flags);
- if (m_config.iSndCryptoKeyLen == 0)
- {
- m_config.iSndCryptoKeyLen = rcv_pbkeylen;
- HLOGC(cnlog.Debug,
- log << CONID() << loghdr
- << ": PBKEYLEN adopted from advertised value: " << m_config.iSndCryptoKeyLen);
- }
- else if (m_config.iSndCryptoKeyLen != rcv_pbkeylen)
- {
- // Conflict. Use SRTO_SENDER flag to check if this side should accept
- // the enforcement, otherwise simply let it win.
- if (!m_config.bDataSender)
- {
- LOGC(cnlog.Warn,
- log << CONID() << loghdr << ": PBKEYLEN conflict - OVERRIDDEN " << m_config.iSndCryptoKeyLen
- << " by " << rcv_pbkeylen << " from PEER (as AGENT is not SRTO_SENDER)");
- m_config.iSndCryptoKeyLen = rcv_pbkeylen;
- }
- else
- {
- LOGC(cnlog.Warn,
- log << CONID() << loghdr << ": PBKEYLEN conflict - keep " << m_config.iSndCryptoKeyLen
- << "; peer-advertised PBKEYLEN " << rcv_pbkeylen << " rejected because Agent is SRTO_SENDER");
- }
- }
- }
- else if (enc_flags != 0)
- {
- LOGC(cnlog.Error, log << CONID() << loghdr << ": IPE: enc_flags outside allowed 2, 3, 4: " << enc_flags);
- }
- else
- {
- HLOGC(cnlog.Debug, log << CONID() << loghdr << ": No encryption flags found in type field: " << typefield);
- }
- }
- // Rendezvous
- void srt::CUDT::rendezvousSwitchState(UDTRequestType& w_rsptype, bool& w_needs_extension, bool& w_needs_hsrsp)
- {
- UDTRequestType req = m_ConnRes.m_iReqType;
- int hs_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType);
- bool has_extension = !!hs_flags; // it holds flags, if no flags, there are no extensions.
- const HandshakeSide &hsd = m_SrtHsSide;
- // Note important possibilities that are considered here:
- // 1. The serial arrangement. This happens when one party has missed the
- // URQ_WAVEAHAND message, it sent its own URQ_WAVEAHAND message, and then the
- // firstmost message it received from the peer is URQ_CONCLUSION, as a response
- // for agent's URQ_WAVEAHAND.
- //
- // In this case, Agent switches to RDV_FINE state and Peer switches to RDV_ATTENTION state.
- //
- // 2. The parallel arrangement. This happens when the URQ_WAVEAHAND message sent
- // by both parties are almost in a perfect synch (a rare, but possible case). In this
- // case, both parties receive one another's URQ_WAVEAHAND message and both switch to
- // RDV_ATTENTION state.
- //
- // It's not possible to predict neither which arrangement will happen, or which
- // party will be RDV_FINE in case when the serial arrangement has happened. What
- // will actually happen will depend on random conditions.
- //
- // No matter this randomity, we have a limited number of possible conditions:
- //
- // Stating that "agent" is the party that has received the URQ_WAVEAHAND in whatever
- // arrangement, we are certain, that "agent" switched to RDV_ATTENTION, and peer:
- //
- // - switched to RDV_ATTENTION state (so, both are in the same state independently)
- // - switched to RDV_FINE state (so, the message interchange is actually more-less sequenced)
- //
- // In particular, there's no possibility of a situation that both are in RDV_FINE state
- // because the agent can switch to RDV_FINE state only if it received URQ_CONCLUSION from
- // the peer, while the peer could not send URQ_CONCLUSION without switching off RDV_WAVING
- // (actually to RDV_ATTENTION). There's also no exit to RDV_FINE from RDV_ATTENTION.
- // DEFAULT STATEMENT: don't attach extensions to URQ_CONCLUSION, neither HSREQ nor HSRSP.
- w_needs_extension = false;
- w_needs_hsrsp = false;
- string reason;
- #if ENABLE_HEAVY_LOGGING
- HLOGC(cnlog.Debug, log << CONID() << "rendezvousSwitchState: HS: " << m_ConnRes.show());
- struct LogAtTheEnd
- {
- CHandShake::RendezvousState ost;
- UDTRequestType orq;
- const CHandShake::RendezvousState &nst;
- const UDTRequestType & nrq;
- bool & needext;
- bool & needrsp;
- string & reason;
- ~LogAtTheEnd()
- {
- HLOGC(cnlog.Debug,
- log << "rendezvousSwitchState: STATE[" << CHandShake::RdvStateStr(ost) << "->"
- << CHandShake::RdvStateStr(nst) << "] REQTYPE[" << RequestTypeStr(orq) << "->"
- << RequestTypeStr(nrq) << "] "
- << "ext:" << (needext ? (needrsp ? "HSRSP" : "HSREQ") : "NONE")
- << (reason == "" ? string() : "reason:" + reason));
- }
- } l_logend = {m_RdvState, req, m_RdvState, w_rsptype, w_needs_extension, w_needs_hsrsp, reason};
- #endif
- switch (m_RdvState)
- {
- case CHandShake::RDV_INVALID:
- return;
- case CHandShake::RDV_WAVING:
- {
- if (req == URQ_WAVEAHAND)
- {
- m_RdvState = CHandShake::RDV_ATTENTION;
- // NOTE: if this->isWinner(), attach HSREQ
- w_rsptype = URQ_CONCLUSION;
- if (hsd == HSD_INITIATOR)
- w_needs_extension = true;
- return;
- }
- if (req == URQ_CONCLUSION)
- {
- m_RdvState = CHandShake::RDV_FINE;
- w_rsptype = URQ_CONCLUSION;
- w_needs_extension = true; // (see below - this needs to craft either HSREQ or HSRSP)
- // if this->isWinner(), then craft HSREQ for that response.
- // if this->isLoser(), then this packet should bring HSREQ, so craft HSRSP for the response.
- if (hsd == HSD_RESPONDER)
- w_needs_hsrsp = true;
- return;
- }
- }
- reason = "WAVING -> WAVEAHAND or CONCLUSION";
- break;
- case CHandShake::RDV_ATTENTION:
- {
- if (req == URQ_WAVEAHAND)
- {
- // This is only possible if the URQ_CONCLUSION sent to the peer
- // was lost on track. The peer is then simply unaware that the
- // agent has switched to ATTENTION state and continues sending
- // waveahands. In this case, just remain in ATTENTION state and
- // retry with URQ_CONCLUSION, as normally.
- w_rsptype = URQ_CONCLUSION;
- if (hsd == HSD_INITIATOR)
- w_needs_extension = true;
- return;
- }
- if (req == URQ_CONCLUSION)
- {
- // We have two possibilities here:
- //
- // WINNER (HSD_INITIATOR): send URQ_AGREEMENT
- if (hsd == HSD_INITIATOR)
- {
- // WINNER should get a response with HSRSP, otherwise this is kinda empty conclusion.
- // If no HSRSP attached, stay in this state.
- if (hs_flags == 0)
- {
- HLOGC(cnlog.Debug,
- log << CONID()
- << "rendezvousSwitchState: {INITIATOR}[ATTENTION] awaits CONCLUSION+HSRSP, got "
- "CONCLUSION, remain in [ATTENTION]");
- w_rsptype = URQ_CONCLUSION;
- w_needs_extension = true; // If you expect to receive HSRSP, continue sending HSREQ
- return;
- }
- m_RdvState = CHandShake::RDV_CONNECTED;
- w_rsptype = URQ_AGREEMENT;
- return;
- }
- // LOSER (HSD_RESPONDER): send URQ_CONCLUSION and attach HSRSP extension, then expect URQ_AGREEMENT
- if (hsd == HSD_RESPONDER)
- {
- // If no HSREQ attached, stay in this state.
- // (Although this seems completely impossible).
- if (hs_flags == 0)
- {
- LOGC(cnlog.Warn,
- log << CONID()
- << "rendezvousSwitchState: (IPE!){RESPONDER}[ATTENTION] awaits CONCLUSION+HSREQ, got "
- "CONCLUSION, remain in [ATTENTION]");
- w_rsptype = URQ_CONCLUSION;
- w_needs_extension = false; // If you received WITHOUT extensions, respond WITHOUT extensions (wait
- // for the right message)
- return;
- }
- m_RdvState = CHandShake::RDV_INITIATED;
- w_rsptype = URQ_CONCLUSION;
- w_needs_extension = true;
- w_needs_hsrsp = true;
- return;
- }
- LOGC(cnlog.Error, log << CONID() << "RENDEZVOUS COOKIE DRAW! Cannot resolve to a valid state.");
- // Fallback for cookie draw
- m_RdvState = CHandShake::RDV_INVALID;
- w_rsptype = URQFailure(SRT_REJ_RDVCOOKIE);
- return;
- }
- if (req == URQ_AGREEMENT)
- {
- // This means that the peer has received our URQ_CONCLUSION, but
- // the agent missed the peer's URQ_CONCLUSION (received only initial
- // URQ_WAVEAHAND).
- if (hsd == HSD_INITIATOR)
- {
- // In this case the missed URQ_CONCLUSION was sent without extensions,
- // whereas the peer received our URQ_CONCLUSION with HSREQ, and therefore
- // it sent URQ_AGREEMENT already with HSRSP. This isn't a problem for
- // us, we can go on with it, especially that the peer is already switched
- // into CHandShake::RDV_CONNECTED state.
- m_RdvState = CHandShake::RDV_CONNECTED;
- // Both sides are connected, no need to send anything anymore.
- w_rsptype = URQ_DONE;
- return;
- }
- if (hsd == HSD_RESPONDER)
- {
- // In this case the missed URQ_CONCLUSION was sent with extensions, so
- // we have to request this once again. Send URQ_CONCLUSION in order to
- // inform the other party that we need the conclusion message once again.
- // The ATTENTION state should be maintained.
- w_rsptype = URQ_CONCLUSION;
- w_needs_extension = true;
- w_needs_hsrsp = true;
- return;
- }
- }
- }
- reason = "ATTENTION -> WAVEAHAND(conclusion), CONCLUSION(agreement/conclusion), AGREEMENT (done/conclusion)";
- break;
- case CHandShake::RDV_FINE:
- {
- // In FINE state we can't receive URQ_WAVEAHAND because if the peer has already
- // sent URQ_CONCLUSION, it's already in CHandShake::RDV_ATTENTION, and in this state it can
- // only send URQ_CONCLUSION, whereas when it isn't in CHandShake::RDV_ATTENTION, it couldn't
- // have sent URQ_CONCLUSION, and if it didn't, the agent wouldn't be in CHandShake::RDV_FINE state.
- if (req == URQ_CONCLUSION)
- {
- // There's only one case when it should receive CONCLUSION in FINE state:
- // When it's the winner. If so, it should then contain HSREQ extension.
- // In case of loser, it shouldn't receive CONCLUSION at all - it should
- // receive AGREEMENT.
- // The winner case, received CONCLUSION + HSRSP - switch to CONNECTED and send AGREEMENT.
- // So, check first if HAS EXTENSION
- bool correct_switch = false;
- if (hsd == HSD_INITIATOR && !has_extension)
- {
- // Received REPEATED empty conclusion that has initially switched it into FINE state.
- // To exit FINE state we need the CONCLUSION message with HSRSP.
- HLOGC(cnlog.Debug,
- log << CONID()
- << "rendezvousSwitchState: {INITIATOR}[FINE] <CONCLUSION without HSRSP. Stay in [FINE], "
- "await CONCLUSION+HSRSP");
- }
- else if (hsd == HSD_RESPONDER)
- {
- // In FINE state the RESPONDER expects only to be sent AGREEMENT.
- // It has previously received CONCLUSION in WAVING state and this has switched
- // it to FINE state. That CONCLUSION message should have contained extension,
- // so if this is a repeated CONCLUSION+HSREQ, it should be responded with
- // CONCLUSION+HSRSP.
- HLOGC(cnlog.Debug,
- log << CONID()
- << "rendezvousSwitchState: {RESPONDER}[FINE] <CONCLUSION. Stay in [FINE], await AGREEMENT");
- }
- else
- {
- correct_switch = true;
- }
- if (!correct_switch)
- {
- w_rsptype = URQ_CONCLUSION;
- // initiator should send HSREQ, responder HSRSP,
- // in both cases extension is needed
- w_needs_extension = true;
- w_needs_hsrsp = hsd == HSD_RESPONDER;
- return;
- }
- m_RdvState = CHandShake::RDV_CONNECTED;
- w_rsptype = URQ_AGREEMENT;
- return;
- }
- if (req == URQ_AGREEMENT)
- {
- // The loser case, the agreement was sent in response to conclusion that
- // already carried over the HSRSP extension.
- // There's a theoretical case when URQ_AGREEMENT can be received in case of
- // parallel arrangement, while the agent is already in CHandShake::RDV_CONNECTED state.
- // This will be dispatched in the main loop and discarded.
- m_RdvState = CHandShake::RDV_CONNECTED;
- w_rsptype = URQ_DONE;
- return;
- }
- }
- reason = "FINE -> CONCLUSION(agreement), AGREEMENT(done)";
- break;
- case CHandShake::RDV_INITIATED:
- {
- // In this state we just wait for URQ_AGREEMENT, which should cause it to
- // switch to CONNECTED. No response required.
- if (req == URQ_AGREEMENT)
- {
- // No matter in which state we'd be, just switch to connected.
- if (m_RdvState == CHandShake::RDV_CONNECTED)
- {
- HLOGC(cnlog.Debug, log << CONID() << "<-- AGREEMENT: already connected");
- }
- else
- {
- HLOGC(cnlog.Debug, log << CONID() << "<-- AGREEMENT: switched to connected");
- }
- m_RdvState = CHandShake::RDV_CONNECTED;
- w_rsptype = URQ_DONE;
- return;
- }
- if (req == URQ_CONCLUSION)
- {
- // Receiving conclusion in this state means that the other party
- // didn't get our conclusion, so send it again, the same as when
- // exiting the ATTENTION state.
- w_rsptype = URQ_CONCLUSION;
- if (hsd == HSD_RESPONDER)
- {
- HLOGC(cnlog.Debug,
- log << CONID()
- << "rendezvousSwitchState: {RESPONDER}[INITIATED] awaits AGREEMENT, "
- "got CONCLUSION, sending CONCLUSION+HSRSP");
- w_needs_extension = true;
- w_needs_hsrsp = true;
- return;
- }
- // Loser, initiated? This may only happen in parallel arrangement, where
- // the agent exchanges empty conclusion messages with the peer, simultaneously
- // exchanging HSREQ-HSRSP conclusion messages. Check if THIS message contained
- // HSREQ, and set responding HSRSP in that case.
- if (hs_flags == 0)
- {
- HLOGC(cnlog.Debug,
- log << CONID()
- << "rendezvousSwitchState: {INITIATOR}[INITIATED] awaits AGREEMENT, "
- "got empty CONCLUSION, STILL RESPONDING CONCLUSION+HSRSP");
- }
- else
- {
- HLOGC(cnlog.Debug,
- log << CONID()
- << "rendezvousSwitchState: {INITIATOR}[INITIATED] awaits AGREEMENT, "
- "got CONCLUSION+HSREQ, responding CONCLUSION+HSRSP");
- }
- w_needs_extension = true;
- w_needs_hsrsp = true;
- return;
- }
- }
- reason = "INITIATED -> AGREEMENT(done)";
- break;
- case CHandShake::RDV_CONNECTED:
- // Do nothing. This theoretically should never happen.
- w_rsptype = URQ_DONE;
- return;
- }
- HLOGC(cnlog.Debug, log << CONID() << "rendezvousSwitchState: INVALID STATE TRANSITION, result: INVALID");
- // All others are treated as errors
- m_RdvState = CHandShake::RDV_WAVING;
- w_rsptype = URQFailure(SRT_REJ_ROGUE);
- }
- /*
- * Timestamp-based Packet Delivery (TsbPd) thread
- * This thread runs only if TsbPd mode is enabled
- * Hold received packets until its time to 'play' them, at PktTimeStamp + TsbPdDelay.
- */
- void * srt::CUDT::tsbpd(void* param)
- {
- CUDT* self = (CUDT*)param;
- THREAD_STATE_INIT("SRT:TsbPd");
- #if ENABLE_BONDING
- // Make the TSBPD thread a "client" of the group,
- // which will ensure that the group will not be physically
- // deleted until this thread exits.
- // NOTE: DO NOT LEAD TO EVER CANCEL THE THREAD!!!
- CUDTUnited::GroupKeeper gkeeper(self->uglobal(), self->m_parent);
- #endif
- CUniqueSync recvdata_lcc (self->m_RecvLock, self->m_RecvDataCond);
- CSync tsbpd_cc(self->m_RcvTsbPdCond, recvdata_lcc.locker());
- self->m_bTsbPdAckWakeup = true;
- while (!self->m_bClosing)
- {
- steady_clock::time_point tsNextDelivery; // Next packet delivery time
- bool rxready = false;
- #if ENABLE_BONDING
- bool shall_update_group = false;
- #endif
- INCREMENT_THREAD_ITERATIONS();
- enterCS(self->m_RcvBufferLock);
- const steady_clock::time_point tnow = steady_clock::now();
- self->m_pRcvBuffer->updRcvAvgDataSize(tnow);
- const srt::CRcvBuffer::PacketInfo info = self->m_pRcvBuffer->getFirstValidPacketInfo();
- const bool is_time_to_deliver = !is_zero(info.tsbpd_time) && (tnow >= info.tsbpd_time);
- tsNextDelivery = info.tsbpd_time;
- if (!self->m_bTLPktDrop)
- {
- rxready = !info.seq_gap && is_time_to_deliver;
- }
- else if (is_time_to_deliver)
- {
- rxready = true;
- if (info.seq_gap)
- {
- const int iDropCnt SRT_ATR_UNUSED = self->rcvDropTooLateUpTo(info.seqno);
- #if ENABLE_BONDING
- shall_update_group = true;
- #endif
- #if ENABLE_LOGGING
- const int64_t timediff_us = count_microseconds(tnow - info.tsbpd_time);
- #if ENABLE_HEAVY_LOGGING
- HLOGC(tslog.Debug,
- log << self->CONID() << "tsbpd: DROPSEQ: up to seqno %" << CSeqNo::decseq(info.seqno) << " ("
- << iDropCnt << " packets) playable at " << FormatTime(info.tsbpd_time) << " delayed "
- << (timediff_us / 1000) << "." << std::setw(3) << std::setfill('0') << (timediff_us % 1000) << " ms");
- #endif
- LOGC(brlog.Warn, log << self->CONID() << "RCV-DROPPED " << iDropCnt << " packet(s). Packet seqno %" << info.seqno
- << " delayed for " << (timediff_us / 1000) << "." << std::setw(3) << std::setfill('0')
- << (timediff_us % 1000) << " ms");
- #endif
- tsNextDelivery = steady_clock::time_point(); // Ready to read, nothing to wait for.
- }
- }
- leaveCS(self->m_RcvBufferLock);
- if (rxready)
- {
- HLOGC(tslog.Debug,
- log << self->CONID() << "tsbpd: PLAYING PACKET seq=" << info.seqno << " (belated "
- << (count_milliseconds(steady_clock::now() - info.tsbpd_time)) << "ms)");
- /*
- * There are packets ready to be delivered
- * signal a waiting "recv" call if there is any data available
- */
- if (self->m_config.bSynRecving)
- {
- recvdata_lcc.notify_one();
- }
- /*
- * Set EPOLL_IN to wakeup any thread waiting on epoll
- */
- self->uglobal().m_EPoll.update_events(self->m_SocketID, self->m_sPollID, SRT_EPOLL_IN, true);
- #if ENABLE_BONDING
- // If this is NULL, it means:
- // - the socket never was a group member
- // - the socket was a group member, but:
- // - was just removed as a part of closure
- // - and will never be member of the group anymore
- // If this is not NULL, it means:
- // - This socket is currently member of the group
- // - This socket WAS a member of the group, though possibly removed from it already, BUT:
- // - the group that this socket IS OR WAS member of is in the GroupKeeper
- // - the GroupKeeper prevents the group from being deleted
- // - it is then completely safe to access the group here,
- // EVEN IF THE SOCKET THAT WAS ITS MEMBER IS BEING DELETED.
- // It is ensured that the group object exists here because GroupKeeper
- // keeps it busy, even if you just closed the socket, remove it as a member
- // or even the group is empty and was explicitly closed.
- if (gkeeper.group)
- {
- // Functions called below will lock m_GroupLock, which in hierarchy
- // lies after m_RecvLock. Must unlock m_RecvLock to be able to lock
- // m_GroupLock inside the calls.
- InvertedLock unrecv(self->m_RecvLock);
- // The current "APP reader" needs to simply decide as to whether
- // the next CUDTGroup::recv() call should return with no blocking or not.
- // When the group is read-ready, it should update its pollers as it sees fit.
- // NOTE: this call will set lock to m_IncludedGroup->m_GroupLock
- HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: GROUP: checking if %" << info.seqno << " makes group readable");
- gkeeper.group->updateReadState(self->m_SocketID, info.seqno);
- if (shall_update_group)
- {
- // A group may need to update the parallelly used idle links,
- // should it have any. Pass the current socket position in order
- // to skip it from the group loop.
- // NOTE: SELF LOCKING.
- gkeeper.group->updateLatestRcv(self->m_parent);
- }
- }
- #endif
- CGlobEvent::triggerEvent();
- tsNextDelivery = steady_clock::time_point(); // Ready to read, nothing to wait for.
- }
- // We may just briefly unlocked the m_RecvLock, so we need to check m_bClosing again to avoid deadlock.
- if (self->m_bClosing)
- break;
- if (!is_zero(tsNextDelivery))
- {
- IF_HEAVY_LOGGING(const steady_clock::duration timediff = tsNextDelivery - tnow);
- /*
- * Buffer at head of queue is not ready to play.
- * Schedule wakeup when it will be.
- */
- self->m_bTsbPdAckWakeup = false;
- HLOGC(tslog.Debug,
- log << self->CONID() << "tsbpd: FUTURE PACKET seq=" << info.seqno
- << " T=" << FormatTime(tsNextDelivery) << " - waiting " << count_milliseconds(timediff) << "ms");
- THREAD_PAUSED();
- tsbpd_cc.wait_until(tsNextDelivery);
- THREAD_RESUMED();
- }
- else
- {
- /*
- * We have just signaled epoll; or
- * receive queue is empty; or
- * next buffer to deliver is not in receive queue (missing packet in sequence).
- *
- * Block until woken up by one of the following event:
- * - All ready-to-play packets have been pulled and EPOLL_IN cleared (then loop to block until next pkt time
- * if any)
- * - New buffers ACKed
- * - Closing the connection
- */
- HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: no data, scheduling wakeup at ack");
- self->m_bTsbPdAckWakeup = true;
- THREAD_PAUSED();
- tsbpd_cc.wait();
- THREAD_RESUMED();
- }
- HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: WAKE UP!!!");
- }
- THREAD_EXIT();
- HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: EXITING");
- return NULL;
- }
- int srt::CUDT::rcvDropTooLateUpTo(int seqno)
- {
- // Make sure that it would not drop over m_iRcvCurrSeqNo, which may break senders.
- if (CSeqNo::seqcmp(seqno, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0)
- seqno = CSeqNo::incseq(m_iRcvCurrSeqNo);
- dropFromLossLists(SRT_SEQNO_NONE, CSeqNo::decseq(seqno));
- const int iDropCnt = m_pRcvBuffer->dropUpTo(seqno);
- if (iDropCnt > 0)
- {
- enterCS(m_StatsLock);
- // Estimate dropped bytes from average payload size.
- const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize();
- m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt));
- leaveCS(m_StatsLock);
- }
- return iDropCnt;
- }
- void srt::CUDT::setInitialRcvSeq(int32_t isn)
- {
- m_iRcvLastAck = isn;
- #ifdef ENABLE_LOGGING
- m_iDebugPrevLastAck = isn;
- #endif
- m_iRcvLastAckAck = isn;
- m_iRcvCurrSeqNo = CSeqNo::decseq(isn);
- sync::ScopedLock rb(m_RcvBufferLock);
- if (m_pRcvBuffer)
- {
- if (!m_pRcvBuffer->empty())
- {
- LOGC(cnlog.Error, log << CONID() << "IPE: setInitialRcvSeq expected empty RCV buffer. Dropping all.");
- const int iDropCnt = m_pRcvBuffer->dropAll();
- const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize();
- sync::ScopedLock sl(m_StatsLock);
- m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt));
- }
- m_pRcvBuffer->setStartSeqNo(isn);
- }
- }
- bool srt::CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException *eout)
- {
- // This will be lazily created due to being the common
- // code with HSv5 rendezvous, in which this will be run
- // in a little bit "randomly selected" moment, but must
- // be run once in the whole connection process.
- if (m_pCryptoControl)
- {
- HLOGC(rslog.Debug, log << CONID() << "prepareConnectionObjects: (lazy) already created.");
- return true;
- }
- // HSv5 is always bidirectional
- const bool bidirectional = (hs.m_iVersion > HS_VERSION_UDT4);
- // HSD_DRAW is received only if this side is listener.
- // If this side is caller with HSv5, HSD_INITIATOR should be passed.
- // If this is a rendezvous connection with HSv5, the handshake role
- // is taken from m_SrtHsSide field.
- if (hsd == HSD_DRAW)
- {
- if (bidirectional)
- {
- hsd = HSD_RESPONDER; // In HSv5, listener is always RESPONDER and caller always INITIATOR.
- }
- else
- {
- hsd = m_config.bDataSender ? HSD_INITIATOR : HSD_RESPONDER;
- }
- }
- if (!createCrypter(hsd, bidirectional)) // Make sure CC is created (lazy)
- {
- if (eout)
- *eout = CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0);
- m_RejectReason = SRT_REJ_RESOURCE;
- return false;
- }
- return true;
- }
- bool srt::CUDT::prepareBuffers(CUDTException* eout)
- {
- if (m_pSndBuffer)
- {
- HLOGC(rslog.Debug, log << CONID() << "prepareBuffers: (lazy) already created.");
- return true;
- }
-
- try
- {
- // CryptoControl has to be initialized and in case of RESPONDER the KM REQ must be processed (interpretSrtHandshake(..)) for the crypto mode to be deduced.
- const int authtag = (m_pCryptoControl && m_pCryptoControl->getCryptoMode() == CSrtConfig::CIPHER_MODE_AES_GCM) ? HAICRYPT_AUTHTAG_MAX : 0;
- m_pSndBuffer = new CSndBuffer(32, m_iMaxSRTPayloadSize, authtag);
- SRT_ASSERT(m_iPeerISN != -1);
- m_pRcvBuffer = new srt::CRcvBuffer(m_iPeerISN, m_config.iRcvBufSize, m_pRcvQueue->m_pUnitQueue, m_config.bMessageAPI);
- // After introducing lite ACK, the sndlosslist may not be cleared in time, so it requires twice a space.
- m_pSndLossList = new CSndLossList(m_iFlowWindowSize * 2);
- m_pRcvLossList = new CRcvLossList(m_config.iFlightFlagSize);
- }
- catch (...)
- {
- // Simply reject.
- if (eout)
- *eout = CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0);
- m_RejectReason = SRT_REJ_RESOURCE;
- return false;
- }
- return true;
- }
- void srt::CUDT::rewriteHandshakeData(const sockaddr_any& peer, CHandShake& w_hs)
- {
- // this is a response handshake
- w_hs.m_iReqType = URQ_CONCLUSION;
- w_hs.m_iMSS = m_config.iMSS;
- w_hs.m_iFlightFlagSize = m_config.flightCapacity();
- w_hs.m_iID = m_SocketID;
- if (w_hs.m_iVersion > HS_VERSION_UDT4)
- {
- // The version is agreed; this code is executed only in case
- // when AGENT is listener. In this case, conclusion response
- // must always contain HSv5 handshake extensions.
- w_hs.m_extension = true;
- }
- CIPAddress::ntop(peer, (w_hs.m_piPeerIP));
- }
- void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, const CPacket& hspkt, CHandShake& w_hs)
- {
- HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: setting up data according to handshake");
- ScopedLock cg(m_ConnectionLock);
- m_tsRcvPeerStartTime = steady_clock::time_point(); // will be set correctly at SRT HS
- // Uses the smaller MSS between the peers
- m_config.iMSS = std::min(m_config.iMSS, w_hs.m_iMSS);
- // exchange info for maximum flow window size
- m_iFlowWindowSize = w_hs.m_iFlightFlagSize;
- m_iPeerISN = w_hs.m_iISN;
- setInitialRcvSeq(m_iPeerISN);
- m_iRcvCurrPhySeqNo = CSeqNo::decseq(w_hs.m_iISN);
- m_PeerID = w_hs.m_iID;
- // use peer's ISN and send it back for security check
- m_iISN = w_hs.m_iISN;
- setInitialSndSeq(m_iISN);
- m_SndLastAck2Time = steady_clock::now();
- // get local IP address and send the peer its IP address (because UDP cannot get local IP address)
- memcpy((m_piSelfIP), w_hs.m_piPeerIP, sizeof m_piSelfIP);
- m_parent->m_SelfAddr = agent;
- CIPAddress::pton((m_parent->m_SelfAddr), m_piSelfIP, peer);
- rewriteHandshakeData(peer, (w_hs));
- int udpsize = m_config.iMSS - CPacket::UDP_HDR_SIZE;
- m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE;
- HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize);
- // Prepare all structures
- if (!prepareConnectionObjects(w_hs, HSD_DRAW, 0))
- {
- HLOGC(cnlog.Debug,
- log << CONID() << "acceptAndRespond: prepareConnectionObjects failed - responding with REJECT.");
- // If the SRT Handshake extension was provided and wasn't interpreted
- // correctly, the connection should be rejected.
- //
- // Respond with the rejection message and exit with exception
- // so that the caller will know that this new socket should be deleted.
- w_hs.m_iReqType = URQFailure(m_RejectReason);
- throw CUDTException(MJ_SETUP, MN_REJECTED, 0);
- }
- // Since now you can use m_pCryptoControl
- CInfoBlock ib;
- ib.m_iIPversion = peer.family();
- CInfoBlock::convert(peer, ib.m_piIP);
- if (m_pCache->lookup(&ib) >= 0)
- {
- m_iSRTT = ib.m_iSRTT;
- m_iRTTVar = ib.m_iSRTT / 2;
- m_iBandwidth = ib.m_iBandwidth;
- }
- #if SRT_DEBUG_RTT
- s_rtt_trace.trace(steady_clock::now(), "Accept", -1, -1,
- m_bIsFirstRTTReceived, -1, m_iSRTT, m_iRTTVar);
- #endif
- m_PeerAddr = peer;
- // This should extract the HSREQ and KMREQ portion in the handshake packet.
- // This could still be a HSv4 packet and contain no such parts, which will leave
- // this entity as "non-SRT-handshaken", and await further HSREQ and KMREQ sent
- // as UMSG_EXT.
- uint32_t kmdata[SRTDATA_MAXSIZE];
- size_t kmdatasize = SRTDATA_MAXSIZE;
- if (!interpretSrtHandshake(w_hs, hspkt, (kmdata), (&kmdatasize)))
- {
- HLOGC(cnlog.Debug,
- log << CONID() << "acceptAndRespond: interpretSrtHandshake failed - responding with REJECT.");
- // If the SRT Handshake extension was provided and wasn't interpreted
- // correctly, the connection should be rejected.
- //
- // Respond with the rejection message and return false from
- // this function so that the caller will know that this new
- // socket should be deleted.
- w_hs.m_iReqType = URQFailure(m_RejectReason);
- throw CUDTException(MJ_SETUP, MN_REJECTED, 0);
- }
- if (!prepareBuffers(NULL))
- {
- HLOGC(cnlog.Debug,
- log << CONID() << "acceptAndRespond: prepareConnectionObjects failed - responding with REJECT.");
- // If the SRT buffers failed to be allocated,
- // the connection must be rejected.
- //
- // Respond with the rejection message and exit with exception
- // so that the caller will know that this new socket should be deleted.
- w_hs.m_iReqType = URQFailure(m_RejectReason);
- throw CUDTException(MJ_SETUP, MN_REJECTED, 0);
- }
- // Synchronize the time NOW because the following function is about
- // to use the start time to pass it to the receiver buffer data.
- bool have_group = false;
- {
- #if ENABLE_BONDING
- ScopedLock cl (uglobal().m_GlobControlLock);
- CUDTGroup* g = m_parent->m_GroupOf;
- if (g)
- {
- // This is the last moment when this can be done.
- // The updateAfterSrtHandshake call will copy the receiver
- // start time to the receiver buffer data, so the correct
- // value must be set before this happens.
- synchronizeWithGroup(g);
- have_group = true;
- }
- #endif
- }
- if (!have_group)
- {
- // This function will be called internally inside
- // synchronizeWithGroup(). This is just more complicated.
- updateAfterSrtHandshake(w_hs.m_iVersion);
- }
- SRT_REJECT_REASON rr = setupCC();
- // UNKNOWN used as a "no error" value
- if (rr != SRT_REJ_UNKNOWN)
- {
- w_hs.m_iReqType = URQFailure(rr);
- m_RejectReason = rr;
- throw CUDTException(MJ_SETUP, MN_REJECTED, 0);
- }
- // And of course, it is connected.
- m_bConnected = true;
- // Register this socket for receiving data packets.
- m_pRNode->m_bOnList = true;
- m_pRcvQueue->setNewEntry(this);
- // Save the handshake in m_ConnRes in case when needs repeating.
- m_ConnRes = w_hs;
- // Send the response to the peer, see listen() for more discussions
- // about this.
- // TODO: Here create CONCLUSION RESPONSE with:
- // - just the UDT handshake, if HS_VERSION_UDT4,
- // - if higher, the UDT handshake, the SRT HSRSP, the SRT KMRSP.
- size_t size = m_iMaxSRTPayloadSize;
- // Allocate the maximum possible memory for an SRT payload.
- // This is a maximum you can send once.
- CPacket response;
- response.setControl(UMSG_HANDSHAKE);
- response.allocate(size);
- // This will serialize the handshake according to its current form.
- HLOGC(cnlog.Debug,
- log << CONID()
- << "acceptAndRespond: creating CONCLUSION response (HSv5: with HSRSP/KMRSP) buffer size=" << size);
- if (!createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, (response), (w_hs)))
- {
- LOGC(cnlog.Error, log << CONID() << "acceptAndRespond: error creating handshake response");
- throw CUDTException(MJ_SETUP, MN_REJECTED, 0);
- }
- // We can safely assign it here stating that this has passed the cookie test.
- m_SourceAddr = hspkt.udpDestAddr();
- #if ENABLE_HEAVY_LOGGING
- {
- // To make sure what REALLY is being sent, parse back the handshake
- // data that have been just written into the buffer.
- CHandShake debughs;
- debughs.load_from(response.m_pcData, response.getLength());
- HLOGC(cnlog.Debug,
- log << CONID() << "acceptAndRespond: sending HS from agent @"
- << debughs.m_iID << " to peer @" << response.m_iID
- << "HS:" << debughs.show()
- << " sourceIP=" << m_SourceAddr.str());
- }
- #endif
- // NOTE: BLOCK THIS instruction in order to cause the final
- // handshake to be missed and cause the problem solved in PR #417.
- // When missed this message, the caller should not accept packets
- // coming as connected, but continue repeated handshake until finally
- // received the listener's handshake.
- addressAndSend((response));
- }
- bool srt::CUDT::frequentLogAllowed(const time_point& tnow) const
- {
- #ifndef SRT_LOG_SLOWDOWN_FREQ_MS
- #define SRT_LOG_SLOWDOWN_FREQ_MS 1000
- #endif
- return (m_tsLogSlowDown + milliseconds_from(SRT_LOG_SLOWDOWN_FREQ_MS)) <= tnow;
- }
- // This function is required to be called when a caller receives an INDUCTION
- // response from the listener and would like to create a CONCLUSION that includes
- // the SRT handshake extension. This extension requires that the crypter object
- // be created, but it's still too early for it to be completely configured.
- // This function then precreates the object so that the handshake extension can
- // be created, as this happens before the completion of the connection (and
- // therefore configuration of the crypter object), which can only take place upon
- // reception of CONCLUSION response from the listener.
- bool srt::CUDT::createCrypter(HandshakeSide side, bool bidirectional)
- {
- // Lazy initialization
- if (m_pCryptoControl)
- return true;
- // Write back this value, when it was just determined.
- m_SrtHsSide = side;
- m_pCryptoControl.reset(new CCryptoControl(m_SocketID));
- // XXX These below are a little bit controversial.
- // These data should probably be filled only upon
- // reception of the conclusion handshake - otherwise
- // they have outdated values.
- m_pCryptoControl->setCryptoSecret(m_config.CryptoSecret);
- if (bidirectional || m_config.bDataSender)
- {
- HLOGC(rslog.Debug, log << CONID() << "createCrypter: setting RCV/SND KeyLen=" << m_config.iSndCryptoKeyLen);
- m_pCryptoControl->setCryptoKeylen(m_config.iSndCryptoKeyLen);
- }
- return m_pCryptoControl->init(side, m_config, bidirectional);
- }
- SRT_REJECT_REASON srt::CUDT::setupCC()
- {
- // Prepare configuration object,
- // Create the CCC object and configure it.
- // UDT also sets back the congestion window: ???
- // m_dCongestionWindow = m_pCC->m_dCWndSize;
- // XXX Not sure about that. May happen that AGENT wants
- // tsbpd mode, but PEER doesn't, even in bidirectional mode.
- // This way, the reception side should get precedense.
- // if (bidirectional || m_config.bDataSender || m_bTwoWayData)
- // m_bPeerTsbPd = m_bTSBPD;
- // SrtCongestion will retrieve whatever parameters it needs
- // from *this.
- bool res = m_CongCtl.select(m_config.sCongestion.str());
- if (!res || !m_CongCtl.configure(this))
- {
- return SRT_REJ_CONGESTION;
- }
- // Configure filter module
- if (!m_config.sPacketFilterConfig.empty())
- {
- // This string, when nonempty, defines that the corrector shall be
- // configured. Otherwise it's left uninitialized.
- // At this point we state everything is checked and the appropriate
- // corrector type is already selected, so now create it.
- HLOGC(pflog.Debug, log << CONID() << "filter: Configuring: " << m_config.sPacketFilterConfig.c_str());
- bool status = true;
- try
- {
- // The filter configurer is build the way that allows to quit immediately
- // exit by exception, but the exception is meant for the filter only.
- status = m_PacketFilter.configure(this, m_pRcvQueue->m_pUnitQueue, m_config.sPacketFilterConfig.str());
- }
- catch (CUDTException& )
- {
- status = false;
- }
- if (!status)
- return SRT_REJ_FILTER;
- m_PktFilterRexmitLevel = m_PacketFilter.arqLevel();
- }
- else
- {
- // When we have no filter, ARQ should work in ALWAYS mode.
- m_PktFilterRexmitLevel = SRT_ARQ_ALWAYS;
- }
- // Override the value of minimum NAK interval, per SrtCongestion's wish.
- // When default 0 value is returned, the current value set by CUDT
- // is preserved.
- const steady_clock::duration min_nak = microseconds_from(m_CongCtl->minNAKInterval());
- if (min_nak != steady_clock::duration::zero())
- m_tdMinNakInterval = min_nak;
- // Update timers
- const steady_clock::time_point currtime = steady_clock::now();
- m_tsLastRspTime.store(currtime);
- m_tsNextACKTime.store(currtime + m_tdACKInterval);
- m_tsNextNAKTime.store(currtime + m_tdNAKInterval);
- m_tsLastRspAckTime = currtime;
- m_tsLastSndTime.store(currtime);
- HLOGC(rslog.Debug,
- log << CONID() << "setupCC: setting parameters: mss=" << m_config.iMSS << " maxCWNDSize/FlowWindowSize="
- << m_iFlowWindowSize << " rcvrate=" << m_iDeliveryRate << "p/s (" << m_iByteDeliveryRate << "B/S)"
- << " rtt=" << m_iSRTT << " bw=" << m_iBandwidth);
- if (!updateCC(TEV_INIT, EventVariant(TEV_INIT_RESET)))
- {
- LOGC(rslog.Error, log << CONID() << "setupCC: IPE: resrouces not yet initialized!");
- return SRT_REJ_IPE;
- }
- return SRT_REJ_UNKNOWN;
- }
- void srt::CUDT::considerLegacySrtHandshake(const steady_clock::time_point &timebase)
- {
- // Do a fast pre-check first - this simply declares that agent uses HSv5
- // and the legacy SRT Handshake is not to be done. Second check is whether
- // agent is sender (=initiator in HSv4).
- if (!isOPT_TsbPd() || !m_config.bDataSender)
- return;
- if (m_iSndHsRetryCnt <= 0)
- {
- HLOGC(cnlog.Debug, log << CONID() << "Legacy HSREQ: not needed, expire counter=" << m_iSndHsRetryCnt);
- return;
- }
- const steady_clock::time_point now = steady_clock::now();
- if (!is_zero(timebase))
- {
- // Then this should be done only if it's the right time,
- // the TSBPD mode is on, and when the counter is "still rolling".
- /*
- * SRT Handshake with peer:
- * If...
- * - we want TsbPd mode; and
- * - we have not tried more than CSRTCC_MAXRETRY times (peer may not be SRT); and
- * - and did not get answer back from peer
- * - last sent handshake req should have been replied (RTT*1.5 elapsed); and
- * then (re-)send handshake request.
- */
- if (timebase > now) // too early
- {
- HLOGC(cnlog.Debug,
- log << CONID() << "Legacy HSREQ: TOO EARLY, will still retry " << m_iSndHsRetryCnt << " times");
- return;
- }
- }
- // If 0 timebase, it means that this is the initial sending with the very first
- // payload packet sent. Send only if this is still set to maximum+1 value.
- else if (m_iSndHsRetryCnt < SRT_MAX_HSRETRY + 1)
- {
- HLOGC(cnlog.Debug,
- log << CONID() << "Legacy HSREQ: INITIAL, REPEATED, so not to be done. Will repeat on sending "
- << m_iSndHsRetryCnt << " times");
- return;
- }
- HLOGC(cnlog.Debug,
- log << CONID() << "Legacy HSREQ: SENDING, will repeat " << m_iSndHsRetryCnt << " times if no response");
- m_iSndHsRetryCnt--;
- m_tsSndHsLastTime = now;
- sendSrtMsg(SRT_CMD_HSREQ);
- }
- void srt::CUDT::checkSndTimers()
- {
- if (m_SrtHsSide == HSD_INITIATOR)
- {
- HLOGC(cnlog.Debug,
- log << CONID() << "checkSndTimers: HS SIDE: INITIATOR, considering legacy handshake with timebase");
- // Legacy method for HSREQ, only if initiator.
- considerLegacySrtHandshake(m_tsSndHsLastTime + microseconds_from(m_iSRTT * 3 / 2));
- }
- else
- {
- HLOGC(cnlog.Debug,
- log << CONID()
- << "checkSndTimers: HS SIDE: " << (m_SrtHsSide == HSD_RESPONDER ? "RESPONDER" : "DRAW (IPE?)")
- << " - not considering legacy handshake");
- }
- // Retransmit KM request after a timeout if there is no response (KM RSP).
- // Or send KM REQ in case of the HSv4.
- ScopedLock lck(m_ConnectionLock);
- if (m_pCryptoControl)
- m_pCryptoControl->sendKeysToPeer(this, SRTT());
- }
- void srt::CUDT::checkSndKMRefresh()
- {
- // Do not apply the regenerated key to the to the receiver context.
- const bool bidir = false;
- if (m_pCryptoControl)
- m_pCryptoControl->regenCryptoKm(this, bidir);
- }
- void srt::CUDT::addressAndSend(CPacket& w_pkt)
- {
- w_pkt.m_iID = m_PeerID;
- setPacketTS(w_pkt, steady_clock::now());
- // NOTE: w_pkt isn't modified in this call,
- // just in CChannel::sendto it's modified in place
- // before sending for performance purposes,
- // and then modification is undone. Logically then
- // there's no modification here.
- m_pSndQueue->sendto(m_PeerAddr, w_pkt, m_SourceAddr);
- }
- // [[using maybe_locked(m_GlobControlLock, if called from GC)]]
- bool srt::CUDT::closeInternal()
- {
- // NOTE: this function is called from within the garbage collector thread.
- if (!m_bOpened)
- {
- return false;
- }
- // IMPORTANT:
- // This function may block indefinitely, if called for a socket
- // that has m_bBroken == false or m_bConnected == true.
- // If it is intended to forcefully close the socket, make sure
- // that it's in response to a broken connection.
- HLOGC(smlog.Debug, log << CONID() << "closing socket");
- if (m_config.Linger.l_onoff != 0)
- {
- const steady_clock::time_point entertime = steady_clock::now();
- HLOGC(smlog.Debug, log << CONID() << "... (linger)");
- while (!m_bBroken && m_bConnected && (m_pSndBuffer->getCurrBufSize() > 0) &&
- (steady_clock::now() - entertime < seconds_from(m_config.Linger.l_linger)))
- {
- // linger has been checked by previous close() call and has expired
- if (m_tsLingerExpiration >= entertime)
- break;
- if (!m_config.bSynSending)
- {
- // if this socket enables asynchronous sending, return immediately and let GC to close it later
- if (is_zero(m_tsLingerExpiration))
- m_tsLingerExpiration = entertime + seconds_from(m_config.Linger.l_linger);
- HLOGC(smlog.Debug,
- log << CONID() << "CUDT::close: linger-nonblocking, setting expire time T="
- << FormatTime(m_tsLingerExpiration));
- return false;
- }
- #ifndef _WIN32
- timespec ts;
- ts.tv_sec = 0;
- ts.tv_nsec = 1000000;
- nanosleep(&ts, NULL);
- #else
- Sleep(1);
- #endif
- }
- }
- // remove this socket from the snd queue
- if (m_bConnected)
- m_pSndQueue->m_pSndUList->remove(this);
- /*
- * update_events below useless
- * removing usock for EPolls right after (update_usocks) clears it (in other HAI patch).
- *
- * What is in EPoll shall be the responsibility of the application, if it want local close event,
- * it would remove the socket from the EPoll after close.
- */
- // Make a copy under a lock because other thread might access it
- // at the same time.
- enterCS(uglobal().m_EPoll.m_EPollLock);
- set<int> epollid = m_sPollID;
- leaveCS(uglobal().m_EPoll.m_EPollLock);
- // trigger any pending IO events.
- HLOGC(smlog.Debug, log << CONID() << "close: SETTING ERR readiness on E" << Printable(epollid));
- uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_ERR, true);
- // then remove itself from all epoll monitoring
- int no_events = 0;
- for (set<int>::iterator i = epollid.begin(); i != epollid.end(); ++i)
- {
- HLOGC(smlog.Debug, log << CONID() << "close: CLEARING subscription on E" << (*i));
- try
- {
- uglobal().m_EPoll.update_usock(*i, m_SocketID, &no_events);
- }
- catch (...)
- {
- // The goal of this loop is to remove all subscriptions in
- // the epoll system to this socket. If it's unsubscribed already,
- // that's even better.
- }
- HLOGC(smlog.Debug, log << CONID() << "close: removing E" << (*i) << " from back-subscribers");
- }
- // Not deleting elements from m_sPollID inside the loop because it invalidates
- // the control iterator of the loop. Instead, all will be removed at once.
- // IMPORTANT: there's theoretically little time between setting ERR readiness
- // and unsubscribing, however if there's an application waiting on this event,
- // it should be informed before this below instruction locks the epoll mutex.
- enterCS(uglobal().m_EPoll.m_EPollLock);
- m_sPollID.clear();
- leaveCS(uglobal().m_EPoll.m_EPollLock);
- // XXX What's this, could any of the above actions make it !m_bOpened?
- if (!m_bOpened)
- {
- return true;
- }
- // Inform the threads handler to stop.
- m_bClosing = true;
- HLOGC(smlog.Debug, log << CONID() << "CLOSING STATE. Acquiring connection lock");
- ScopedLock connectguard(m_ConnectionLock);
- // Signal the sender and recver if they are waiting for data.
- releaseSynch();
- HLOGC(smlog.Debug, log << CONID() << "CLOSING, removing from listener/connector");
- if (m_bListening)
- {
- m_bListening = false;
- m_pRcvQueue->removeListener(this);
- }
- else if (m_bConnecting)
- {
- m_pRcvQueue->removeConnector(m_SocketID);
- }
- if (m_bConnected)
- {
- if (!m_bShutdown)
- {
- HLOGC(smlog.Debug, log << CONID() << "CLOSING - sending SHUTDOWN to the peer @" << m_PeerID);
- sendCtrl(UMSG_SHUTDOWN);
- }
- // Store current connection information.
- CInfoBlock ib;
- ib.m_iIPversion = m_PeerAddr.family();
- CInfoBlock::convert(m_PeerAddr, ib.m_piIP);
- ib.m_iSRTT = m_iSRTT;
- ib.m_iBandwidth = m_iBandwidth;
- m_pCache->update(&ib);
- #if SRT_DEBUG_RTT
- s_rtt_trace.trace(steady_clock::now(), "Cache", -1, -1,
- m_bIsFirstRTTReceived, -1, m_iSRTT, -1);
- #endif
- m_bConnected = false;
- }
- HLOGC(smlog.Debug, log << CONID() << "CLOSING, joining send/receive threads");
- // waiting all send and recv calls to stop
- ScopedLock sendguard(m_SendLock);
- ScopedLock recvguard(m_RecvLock);
- // Locking m_RcvBufferLock to protect calling to m_pCryptoControl->decrypt((packet))
- // from the processData(...) function while resetting Crypto Control.
- enterCS(m_RcvBufferLock);
- if (m_pCryptoControl)
- m_pCryptoControl->close();
- m_pCryptoControl.reset();
- leaveCS(m_RcvBufferLock);
- m_uPeerSrtVersion = SRT_VERSION_UNK;
- m_tsRcvPeerStartTime = steady_clock::time_point();
- m_bOpened = false;
- return true;
- }
- int srt::CUDT::receiveBuffer(char *data, int len)
- {
- if (!m_CongCtl->checkTransArgs(SrtCongestion::STA_BUFFER, SrtCongestion::STAD_RECV, data, len, SRT_MSGTTL_INF, false))
- throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0);
- if (isOPT_TsbPd())
- {
- LOGP(arlog.Error, "recv: This function is not intended to be used in Live mode with TSBPD.");
- throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0);
- }
- UniqueLock recvguard(m_RecvLock);
- if ((m_bBroken || m_bClosing) && !isRcvBufferReady())
- {
- if (m_bShutdown)
- {
- // For stream API, return 0 as a sign of EOF for transmission.
- // That's a bit controversial because theoretically the
- // UMSG_SHUTDOWN message may be lost as every UDP packet, although
- // another theory states that this will never happen because this
- // packet has a total size of 42 bytes and such packets are
- // declared as never dropped - but still, this is UDP so there's no
- // guarantee.
- // The most reliable way to inform the party that the transmission
- // has ended would be to send a single empty packet (that is,
- // a data packet that contains only an SRT header in the UDP
- // payload), which is a normal data packet that can undergo
- // normal sequence check and retransmission rules, so it's ensured
- // that this packet will be received. Receiving such a packet should
- // make this function return 0, potentially also without breaking
- // the connection and potentially also with losing no ability to
- // send some larger portion of data next time.
- HLOGC(arlog.Debug, log << CONID() << "STREAM API, SHUTDOWN: marking as EOF");
- return 0;
- }
- HLOGC(arlog.Debug,
- log << CONID() << (m_config.bMessageAPI ? "MESSAGE" : "STREAM") << " API, " << (m_bShutdown ? "" : "no")
- << " SHUTDOWN. Reporting as BROKEN.");
- throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
- }
- CSync rcond (m_RecvDataCond, recvguard);
- CSync tscond (m_RcvTsbPdCond, recvguard);
- if (!isRcvBufferReady())
- {
- if (!m_config.bSynRecving)
- {
- throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0);
- }
- // Kick TsbPd thread to schedule next wakeup (if running)
- if (m_config.iRcvTimeOut < 0)
- {
- THREAD_PAUSED();
- while (stillConnected() && !isRcvBufferReady())
- {
- // Do not block forever, check connection status each 1 sec.
- rcond.wait_for(seconds_from(1));
- }
- THREAD_RESUMED();
- }
- else
- {
- const steady_clock::time_point exptime =
- steady_clock::now() + milliseconds_from(m_config.iRcvTimeOut);
- THREAD_PAUSED();
- while (stillConnected() && !isRcvBufferReady())
- {
- if (!rcond.wait_until(exptime)) // NOT means "not received a signal"
- break; // timeout
- }
- THREAD_RESUMED();
- }
- }
- // throw an exception if not connected
- if (!m_bConnected)
- throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
- if ((m_bBroken || m_bClosing) && !isRcvBufferReady())
- {
- // See at the beginning
- if (!m_config.bMessageAPI && m_bShutdown)
- {
- HLOGC(arlog.Debug, log << CONID() << "STREAM API, SHUTDOWN: marking as EOF");
- return 0;
- }
- HLOGC(arlog.Debug,
- log << CONID() << (m_config.bMessageAPI ? "MESSAGE" : "STREAM") << " API, " << (m_bShutdown ? "" : "no")
- << " SHUTDOWN. Reporting as BROKEN.");
- throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
- }
- enterCS(m_RcvBufferLock);
- const int res = m_pRcvBuffer->readBuffer(data, len);
- leaveCS(m_RcvBufferLock);
- /* Kick TsbPd thread to schedule next wakeup (if running) */
- if (m_bTsbPd)
- {
- HLOGP(tslog.Debug, "Ping TSBPD thread to schedule wakeup");
- tscond.notify_one_locked(recvguard);
- }
- else
- {
- HLOGP(tslog.Debug, "NOT pinging TSBPD - not set");
- }
- if (!isRcvBufferReady())
- {
- // read is not available any more
- uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false);
- }
- if ((res <= 0) && (m_config.iRcvTimeOut >= 0))
- throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0);
- return res;
- }
- // [[using maybe_locked(CUDTGroup::m_GroupLock, m_parent->m_GroupOf != NULL)]];
- // [[using locked(m_SendLock)]];
- int srt::CUDT::sndDropTooLate()
- {
- if (!m_bPeerTLPktDrop)
- return 0;
- if (!m_config.bMessageAPI)
- {
- LOGC(aslog.Error, log << CONID() << "The SRTO_TLPKTDROP flag can only be used with message API.");
- throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0);
- }
- const time_point tnow = steady_clock::now();
- const int buffdelay_ms = (int) count_milliseconds(m_pSndBuffer->getBufferingDelay(tnow));
- // high threshold (msec) at tsbpd_delay plus sender/receiver reaction time (2 * 10ms)
- // Minimum value must accommodate an I-Frame (~8 x average frame size)
- // >>need picture rate or app to set min treshold
- // >>using 1 sec for worse case 1 frame using all bit budget.
- // picture rate would be useful in auto SRT setting for min latency
- // XXX Make SRT_TLPKTDROP_MINTHRESHOLD_MS option-configurable
- const int threshold_ms = (m_config.iSndDropDelay >= 0)
- ? std::max(m_iPeerTsbPdDelay_ms + m_config.iSndDropDelay, +SRT_TLPKTDROP_MINTHRESHOLD_MS)
- + (2 * COMM_SYN_INTERVAL_US / 1000)
- : 0;
- if (threshold_ms == 0 || buffdelay_ms <= threshold_ms)
- return 0;
- // protect packet retransmission
- ScopedLock rcvlck(m_RecvAckLock);
- int dbytes;
- int32_t first_msgno;
- const int dpkts = m_pSndBuffer->dropLateData((dbytes), (first_msgno), tnow - milliseconds_from(threshold_ms));
- if (dpkts <= 0)
- return 0;
- // If some packets were dropped update stats, socket state, loss list and the parent group if any.
- enterCS(m_StatsLock);
- m_stats.sndr.dropped.count(dbytes);;
- leaveCS(m_StatsLock);
- IF_HEAVY_LOGGING(const int32_t realack = m_iSndLastDataAck);
- const int32_t fakeack = CSeqNo::incseq(m_iSndLastDataAck, dpkts);
- m_iSndLastAck = fakeack;
- m_iSndLastDataAck = fakeack;
- const int32_t minlastack = CSeqNo::decseq(m_iSndLastDataAck);
- m_pSndLossList->removeUpTo(minlastack);
- /* If we dropped packets not yet sent, advance current position */
- // THIS MEANS: m_iSndCurrSeqNo = MAX(m_iSndCurrSeqNo, m_iSndLastDataAck-1)
- if (CSeqNo::seqcmp(m_iSndCurrSeqNo, minlastack) < 0)
- {
- m_iSndCurrSeqNo = minlastack;
- }
- HLOGC(aslog.Debug,
- log << CONID() << "SND-DROP: %(" << realack << "-" << m_iSndCurrSeqNo << ") n=" << dpkts << "pkt " << dbytes
- << "B, span=" << buffdelay_ms << " ms, FIRST #" << first_msgno);
- #if ENABLE_BONDING
- // This is done with a presumption that the group
- // exists and if this is not NULL, it means that this
- // function was called with locked m_GroupLock, as sendmsg2
- // function was called from inside CUDTGroup::send, which
- // locks the whole function.
- //
- // XXX This is true only because all existing groups are managed
- // groups, that is, sockets cannot be added or removed from group
- // manually, nor can send/recv operation be done on a single socket
- // from the API call directly. This should be extra verified, if that
- // changes in the future.
- //
- if (m_parent->m_GroupOf)
- {
- // What's important is that the lock on GroupLock cannot be applied
- // here, both because it might be applied already, that is, according
- // to the condition defined at this function's header, it is applied
- // under this condition. Hence ackMessage can be defined as 100% locked.
- m_parent->m_GroupOf->ackMessage(first_msgno);
- }
- #endif
- return dpkts;
- }
- int srt::CUDT::sendmsg(const char *data, int len, int msttl, bool inorder, int64_t srctime)
- {
- SRT_MSGCTRL mctrl = srt_msgctrl_default;
- mctrl.msgttl = msttl;
- mctrl.inorder = inorder;
- mctrl.srctime = srctime;
- return this->sendmsg2(data, len, (mctrl));
- }
- // [[using maybe_locked(CUDTGroup::m_GroupLock, m_parent->m_GroupOf != NULL)]]
- // GroupLock is applied when this function is called from inside CUDTGroup::send,
- // which is the only case when the m_parent->m_GroupOf is not NULL.
- int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl)
- {
- // throw an exception if not connected
- if (m_bBroken || m_bClosing)
- throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
- else if (!m_bConnected || !m_CongCtl.ready())
- throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
- if (len <= 0)
- {
- LOGC(aslog.Error, log << CONID() << "INVALID: Data size for sending declared with length: " << len);
- return 0;
- }
- if (w_mctrl.msgno != -1) // most unlikely, unless you use balancing groups
- {
- if (w_mctrl.msgno < 1 || w_mctrl.msgno > MSGNO_SEQ_MAX)
- {
- LOGC(aslog.Error,
- log << CONID() << "INVALID forced msgno " << w_mctrl.msgno << ": can be -1 (trap) or <1..."
- << MSGNO_SEQ_MAX << ">");
- throw CUDTException(MJ_NOTSUP, MN_INVAL);
- }
- }
- int msttl = w_mctrl.msgttl;
- bool inorder = w_mctrl.inorder;
- // Sendmsg isn't restricted to the congctl type, however the congctl
- // may want to have something to say here.
- // NOTE: SrtCongestion is also allowed to throw CUDTException() by itself!
- {
- SrtCongestion::TransAPI api = SrtCongestion::STA_MESSAGE;
- CodeMinor mn = MN_INVALMSGAPI;
- if (!m_config.bMessageAPI)
- {
- api = SrtCongestion::STA_BUFFER;
- mn = MN_INVALBUFFERAPI;
- }
- if (!m_CongCtl->checkTransArgs(api, SrtCongestion::STAD_SEND, data, len, msttl, inorder))
- throw CUDTException(MJ_NOTSUP, mn, 0);
- }
- // NOTE: the length restrictions differ in STREAM API and in MESSAGE API:
- // - STREAM API:
- // At least 1 byte free sending buffer space is needed
- // (in practice, one unit buffer of 1456 bytes).
- // This function will send as much as possible, and return
- // how much was actually sent.
- // - MESSAGE API:
- // At least so many bytes free in the sending buffer is needed,
- // as the length of the data, otherwise this function will block
- // or return MJ_AGAIN until this condition is satisfied. The EXACTLY
- // such number of data will be then written out, and this function
- // will effectively return either -1 (error) or the value of 'len'.
- // This call will be also rejected from upside when trying to send
- // out a message of a length that exceeds the total size of the sending
- // buffer (configurable by SRTO_SNDBUF).
- if (m_config.bMessageAPI && len > int(m_config.iSndBufSize * m_iMaxSRTPayloadSize))
- {
- LOGC(aslog.Error,
- log << CONID() << "Message length (" << len << ") exceeds the size of sending buffer: "
- << (m_config.iSndBufSize * m_iMaxSRTPayloadSize) << ". Use SRTO_SNDBUF if needed.");
- throw CUDTException(MJ_NOTSUP, MN_XSIZE, 0);
- }
- /* XXX
- This might be worth preserving for several occasions, but it
- must be at least conditional because it breaks backward compat.
- if (!m_pCryptoControl || !m_pCryptoControl->isSndEncryptionOK())
- {
- LOGC(aslog.Error, log << "Encryption is required, but the peer did not supply correct credentials. Sending
- rejected."); throw CUDTException(MJ_SETUP, MN_SECURITY, 0);
- }
- */
- UniqueLock sendguard(m_SendLock);
- if (m_pSndBuffer->getCurrBufSize() == 0)
- {
- // delay the EXP timer to avoid mis-fired timeout
- ScopedLock ack_lock(m_RecvAckLock);
- m_tsLastRspAckTime = steady_clock::now();
- m_iReXmitCount = 1;
- }
- // sndDropTooLate(...) may lock m_RecvAckLock
- // to modify m_pSndBuffer and m_pSndLossList
- const int iPktsTLDropped SRT_ATR_UNUSED = sndDropTooLate();
- // For MESSAGE API the minimum outgoing buffer space required is
- // the size that can carry over the whole message as passed here.
- // Otherwise it is allowed to send less bytes.
- const int iNumPktsRequired = m_config.bMessageAPI ? m_pSndBuffer->countNumPacketsRequired(len) : 1;
- if (m_bTsbPd && iNumPktsRequired > 1)
- {
- LOGC(aslog.Error,
- log << CONID() << "Message length (" << len << ") can't fit into a single data packet ("
- << m_pSndBuffer->getMaxPacketLen() << " bytes max).");
- throw CUDTException(MJ_NOTSUP, MN_XSIZE, 0);
- }
- if (sndBuffersLeft() < iNumPktsRequired)
- {
- //>>We should not get here if SRT_ENABLE_TLPKTDROP
- // XXX Check if this needs to be removed, or put to an 'else' condition for m_bTLPktDrop.
- if (!m_config.bSynSending)
- throw CUDTException(MJ_AGAIN, MN_WRAVAIL, 0);
- {
- // wait here during a blocking sending
- UniqueLock sendblock_lock (m_SendBlockLock);
- if (m_config.iSndTimeOut < 0)
- {
- while (stillConnected() && sndBuffersLeft() < iNumPktsRequired && m_bPeerHealth)
- m_SendBlockCond.wait(sendblock_lock);
- }
- else
- {
- const steady_clock::time_point exptime =
- steady_clock::now() + milliseconds_from(m_config.iSndTimeOut);
- THREAD_PAUSED();
- while (stillConnected() && sndBuffersLeft() < iNumPktsRequired && m_bPeerHealth)
- {
- if (!m_SendBlockCond.wait_until(sendblock_lock, exptime))
- break;
- }
- THREAD_RESUMED();
- }
- }
- // check the connection status
- if (m_bBroken || m_bClosing)
- throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
- else if (!m_bConnected)
- throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
- else if (!m_bPeerHealth)
- {
- m_bPeerHealth = true;
- throw CUDTException(MJ_PEERERROR);
- }
- /*
- * The code below is to return ETIMEOUT when blocking mode could not get free buffer in time.
- * If no free buffer available in non-blocking mode, we alredy returned. If buffer available,
- * we test twice if this code is outside the else section.
- * This fix move it in the else (blocking-mode) section
- */
- if (sndBuffersLeft() < iNumPktsRequired)
- {
- if (m_config.iSndTimeOut >= 0)
- throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0);
- // XXX This looks very weird here, however most likely
- // this will happen only in the following case, when
- // the above loop has been interrupted, which happens when:
- // 1. The buffers left gets enough for minlen - but this is excluded
- // in the first condition here.
- // 2. In the case of sending timeout, the above loop was interrupted
- // due to reaching timeout, but this is excluded by the second
- // condition here
- // 3. The 'stillConnected()' or m_bPeerHealth condition is false, of which:
- // - broken/closing status is checked and responded with CONNECTION/CONNLOST
- // - not connected status is checked and responded with CONNECTION/NOCONN
- // - m_bPeerHealth condition is checked and responded with PEERERROR
- //
- // ERGO: never happens?
- LOGC(aslog.Fatal,
- log << CONID()
- << "IPE: sendmsg: the loop exited, while not enough size, still connected, peer healthy. "
- "Impossible.");
- return 0;
- }
- }
- // If the sender's buffer is empty,
- // record total time used for sending
- if (m_pSndBuffer->getCurrBufSize() == 0)
- {
- ScopedLock lock(m_StatsLock);
- m_stats.sndDurationCounter = steady_clock::now();
- }
- int size = len;
- if (!m_config.bMessageAPI)
- {
- // For STREAM API it's allowed to send less bytes than the given buffer.
- // Just return how many bytes were actually scheduled for writing.
- // XXX May be reasonable to add a flag that requires that the function
- // not return until the buffer is sent completely.
- size = min(len, sndBuffersLeft() * m_iMaxSRTPayloadSize);
- }
- {
- ScopedLock recvAckLock(m_RecvAckLock);
- // insert the user buffer into the sending list
- int32_t seqno = m_iSndNextSeqNo;
- IF_HEAVY_LOGGING(int32_t orig_seqno = seqno);
- IF_HEAVY_LOGGING(steady_clock::time_point ts_srctime =
- steady_clock::time_point() + microseconds_from(w_mctrl.srctime));
- #if ENABLE_BONDING
- // Check if seqno has been set, in case when this is a group sender.
- // If the sequence is from the past towards the "next sequence",
- // simply return the size, pretending that it has been sent.
- // NOTE: it's assumed that if this is a group member, then
- // an attempt to call srt_sendmsg2 has been rejected, and so
- // the pktseq field has been set by the internal group sender function.
- if (m_parent->m_GroupOf
- && w_mctrl.pktseq != SRT_SEQNO_NONE
- && m_iSndNextSeqNo != SRT_SEQNO_NONE)
- {
- if (CSeqNo::seqcmp(w_mctrl.pktseq, seqno) < 0)
- {
- HLOGC(aslog.Debug, log << CONID() << "sock:SENDING (NOT): group-req %" << w_mctrl.pktseq
- << " OLDER THAN next expected %" << seqno << " - FAKE-SENDING.");
- return size;
- }
- }
- #endif
- // Set this predicted next sequence to the control information.
- // It's the sequence of the FIRST (!) packet from all packets used to send
- // this buffer. Values from this field will be monotonic only if you always
- // have one packet per buffer (as it's in live mode).
- w_mctrl.pktseq = seqno;
- // Now seqno is the sequence to which it was scheduled
- // XXX Conversion from w_mctrl.srctime -> steady_clock::time_point need not be accurrate.
- HLOGC(aslog.Debug, log << CONID() << "buf:SENDING (BEFORE) srctime:"
- << (w_mctrl.srctime ? FormatTime(ts_srctime) : "none")
- << " DATA SIZE: " << size << " sched-SEQUENCE: " << seqno
- << " STAMP: " << BufferStamp(data, size));
- if (w_mctrl.srctime && w_mctrl.srctime < count_microseconds(m_stats.tsStartTime.time_since_epoch()))
- {
- LOGC(aslog.Error,
- log << CONID() << "Wrong source time was provided. Sending is rejected.");
- throw CUDTException(MJ_NOTSUP, MN_INVALMSGAPI);
- }
- if (w_mctrl.srctime && (!m_config.bMessageAPI || !m_bTsbPd))
- {
- HLOGC(
- aslog.Warn,
- log << CONID()
- << "Source time can only be used with TSBPD and Message API enabled. Using default time instead.");
- w_mctrl.srctime = 0;
- }
- // w_mctrl.seqno is INPUT-OUTPUT value:
- // - INPUT: the current sequence number to be placed for the next scheduled packet
- // - OUTPUT: value of the sequence number to be put on the first packet at the next sendmsg2 call.
- // We need to supply to the output the value that was STAMPED ON THE PACKET,
- // which is seqno. In the output we'll get the next sequence number.
- m_pSndBuffer->addBuffer(data, size, (w_mctrl));
- m_iSndNextSeqNo = w_mctrl.pktseq;
- w_mctrl.pktseq = seqno;
- HLOGC(aslog.Debug, log << CONID() << "buf:SENDING srctime:" << FormatTime(ts_srctime)
- << " size=" << size << " #" << w_mctrl.msgno << " SCHED %" << orig_seqno
- << "(>> %" << seqno << ") !" << BufferStamp(data, size));
- if (sndBuffersLeft() < 1) // XXX Not sure if it should test if any space in the buffer, or as requried.
- {
- // write is not available any more
- uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, false);
- }
- }
- // Insert this socket to the snd list if it is not on the list already.
- // m_pSndUList->pop may lock CSndUList::m_ListLock and then m_RecvAckLock
- m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE);
- #ifdef SRT_ENABLE_ECN
- // IF there was a packet drop on the sender side, report congestion to the app.
- if (iPktsTLDropped > 0)
- {
- LOGC(aslog.Error, log << CONID() << "sendmsg2: CONGESTION; reporting error");
- throw CUDTException(MJ_AGAIN, MN_CONGESTION, 0);
- }
- #endif /* SRT_ENABLE_ECN */
- HLOGC(aslog.Debug, log << CONID() << "sock:SENDING (END): success, size=" << size);
- return size;
- }
- int srt::CUDT::recv(char* data, int len)
- {
- SRT_MSGCTRL mctrl = srt_msgctrl_default;
- return recvmsg2(data, len, (mctrl));
- }
- int srt::CUDT::recvmsg(char* data, int len, int64_t& srctime)
- {
- SRT_MSGCTRL mctrl = srt_msgctrl_default;
- int res = recvmsg2(data, len, (mctrl));
- srctime = mctrl.srctime;
- return res;
- }
- // [[using maybe_locked(CUDTGroup::m_GroupLock, m_parent->m_GroupOf != NULL)]]
- // GroupLock is applied when this function is called from inside CUDTGroup::recv,
- // which is the only case when the m_parent->m_GroupOf is not NULL.
- int srt::CUDT::recvmsg2(char* data, int len, SRT_MSGCTRL& w_mctrl)
- {
- // Check if the socket is a member of a receiver group.
- // If so, then reading by receiveMessage is disallowed.
- #if ENABLE_BONDING
- if (m_parent->m_GroupOf && m_parent->m_GroupOf->isGroupReceiver())
- {
- LOGP(arlog.Error, "recv*: This socket is a receiver group member. Use group ID, NOT socket ID.");
- throw CUDTException(MJ_NOTSUP, MN_INVALMSGAPI, 0);
- }
- #endif
- if (!m_bConnected || !m_CongCtl.ready())
- throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
- if (len <= 0)
- {
- LOGC(arlog.Error, log << CONID() << "Length of '" << len << "' supplied to srt_recvmsg.");
- throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
- }
- if (m_config.bMessageAPI)
- return receiveMessage(data, len, (w_mctrl));
- return receiveBuffer(data, len);
- }
- // [[using locked(m_RcvBufferLock)]]
- size_t srt::CUDT::getAvailRcvBufferSizeNoLock() const
- {
- return m_pRcvBuffer->getAvailSize(m_iRcvLastAck);
- }
- bool srt::CUDT::isRcvBufferReady() const
- {
- ScopedLock lck(m_RcvBufferLock);
- return m_pRcvBuffer->isRcvDataReady(steady_clock::now());
- }
- bool srt::CUDT::isRcvBufferReadyNoLock() const
- {
- return m_pRcvBuffer->isRcvDataReady(steady_clock::now());
- }
- // int by_exception: accepts values of CUDTUnited::ErrorHandling:
- // - 0 - by return value
- // - 1 - by exception
- // - 2 - by abort (unused)
- int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_exception)
- {
- // Recvmsg isn't restricted to the congctl type, it's the most
- // basic method of passing the data. You can retrieve data as
- // they come in, however you need to match the size of the buffer.
- // Note: if by_exception = ERH_RETURN, this would still break it
- // by exception. The intention of by_exception isn't to prevent
- // exceptions here, but to intercept the erroneous situation should
- // it be handled by the caller in a less than general way. As this
- // is only used internally, we state that the problem that would be
- // handled by exception here should not happen, and in case if it does,
- // it's a bug to fix, so the exception is nothing wrong.
- if (!m_CongCtl->checkTransArgs(SrtCongestion::STA_MESSAGE, SrtCongestion::STAD_RECV, data, len, SRT_MSGTTL_INF, false))
- throw CUDTException(MJ_NOTSUP, MN_INVALMSGAPI, 0);
- UniqueLock recvguard (m_RecvLock);
- CSync tscond (m_RcvTsbPdCond, recvguard);
- /* XXX DEBUG STUFF - enable when required
- char charbool[2] = {'0', '1'};
- char ptrn [] = "RECVMSG/BEGIN BROKEN 1 CONN 1 CLOSING 1 SYNCR 1 NMSG ";
- int pos [] = {21, 28, 38, 46, 53};
- ptrn[pos[0]] = charbool[m_bBroken];
- ptrn[pos[1]] = charbool[m_bConnected];
- ptrn[pos[2]] = charbool[m_bClosing];
- ptrn[pos[3]] = charbool[m_config.m_bSynRecving];
- int wrtlen = sprintf(ptrn + pos[4], "%d", m_pRcvBuffer->getRcvMsgNum());
- strcpy(ptrn + pos[4] + wrtlen, "\n");
- fputs(ptrn, stderr);
- // */
- if (m_bBroken || m_bClosing)
- {
- HLOGC(arlog.Debug, log << CONID() << "receiveMessage: CONNECTION BROKEN - reading from recv buffer just for formality");
- enterCS(m_RcvBufferLock);
- const int res = (m_pRcvBuffer->isRcvDataReady(steady_clock::now()))
- ? m_pRcvBuffer->readMessage(data, len, &w_mctrl)
- : 0;
- leaveCS(m_RcvBufferLock);
- // Kick TsbPd thread to schedule next wakeup (if running)
- if (m_bTsbPd)
- {
- HLOGP(tslog.Debug, "Ping TSBPD thread to schedule wakeup");
- tscond.notify_one_locked(recvguard);
- }
- else
- {
- HLOGP(tslog.Debug, "NOT pinging TSBPD - not set");
- }
- if (!isRcvBufferReady())
- {
- // read is not available any more
- uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false);
- }
- if (res == 0)
- {
- if (!m_config.bMessageAPI && m_bShutdown)
- return 0;
- // Forced to return error instead of throwing exception.
- if (!by_exception)
- return APIError(MJ_CONNECTION, MN_CONNLOST, 0);
- throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
- }
- else
- return res;
- }
- if (!m_config.bSynRecving)
- {
- HLOGC(arlog.Debug, log << CONID() << "receiveMessage: BEGIN ASYNC MODE. Going to extract payload size=" << len);
- enterCS(m_RcvBufferLock);
- const int res = (m_pRcvBuffer->isRcvDataReady(steady_clock::now()))
- ? m_pRcvBuffer->readMessage(data, len, &w_mctrl)
- : 0;
- leaveCS(m_RcvBufferLock);
- HLOGC(arlog.Debug, log << CONID() << "AFTER readMsg: (NON-BLOCKING) result=" << res);
- if (res == 0)
- {
- // read is not available any more
- // Kick TsbPd thread to schedule next wakeup (if running)
- if (m_bTsbPd)
- {
- HLOGP(arlog.Debug, "receiveMessage: nothing to read, kicking TSBPD, return AGAIN");
- tscond.notify_one_locked(recvguard);
- }
- else
- {
- HLOGP(arlog.Debug, "receiveMessage: nothing to read, return AGAIN");
- }
- // Shut up EPoll if no more messages in non-blocking mode
- uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false);
- // Forced to return 0 instead of throwing exception, in case of AGAIN/READ
- if (!by_exception)
- return 0;
- throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0);
- }
- if (!isRcvBufferReady())
- {
- // Kick TsbPd thread to schedule next wakeup (if running)
- if (m_bTsbPd)
- {
- HLOGP(arlog.Debug, "receiveMessage: DATA READ, but nothing more - kicking TSBPD.");
- tscond.notify_one_locked(recvguard);
- }
- else
- {
- HLOGP(arlog.Debug, "receiveMessage: DATA READ, but nothing more");
- }
- // Shut up EPoll if no more messages in non-blocking mode
- uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false);
- // After signaling the tsbpd for ready data, report the bandwidth.
- #if ENABLE_HEAVY_LOGGING
- double bw = Bps2Mbps(int64_t(m_iBandwidth) * m_iMaxSRTPayloadSize );
- HLOGC(arlog.Debug, log << CONID() << "CURRENT BANDWIDTH: " << bw << "Mbps (" << m_iBandwidth << " buffers per second)");
- #endif
- }
- return res;
- }
- HLOGC(arlog.Debug, log << CONID() << "receiveMessage: BEGIN SYNC MODE. Going to extract payload size max=" << len);
- int res = 0;
- bool timeout = false;
- // Do not block forever, check connection status each 1 sec.
- const steady_clock::duration recv_timeout = m_config.iRcvTimeOut < 0 ? seconds_from(1) : milliseconds_from(m_config.iRcvTimeOut);
- CSync recv_cond (m_RecvDataCond, recvguard);
- do
- {
- if (stillConnected() && !timeout && !m_pRcvBuffer->isRcvDataReady(steady_clock::now()))
- {
- /* Kick TsbPd thread to schedule next wakeup (if running) */
- if (m_bTsbPd)
- {
- // XXX Experimental, so just inform:
- // Check if the last check of isRcvDataReady has returned any "next time for a packet".
- // If so, then it means that TSBPD has fallen asleep only up to this time, so waking it up
- // would be "spurious". If a new packet comes ahead of the packet which's time is returned
- // in tstime (as TSBPD sleeps up to then), the procedure that receives it is responsible
- // of kicking TSBPD.
- // bool spurious = (tstime != 0);
- HLOGC(tslog.Debug, log << CONID() << "receiveMessage: KICK tsbpd");
- tscond.notify_one_locked(recvguard);
- }
- THREAD_PAUSED();
- do
- {
- // `wait_for(recv_timeout)` wouldn't be correct here. Waiting should be
- // only until the time that is now + timeout since the first moment
- // when this started, or sliced-waiting for 1 second, if timtout is
- // higher than this.
- const steady_clock::time_point exptime = steady_clock::now() + recv_timeout;
- HLOGC(tslog.Debug,
- log << CONID() << "receiveMessage: fall asleep up to TS=" << FormatTime(exptime)
- << " lock=" << (&m_RecvLock) << " cond=" << (&m_RecvDataCond));
- if (!recv_cond.wait_until(exptime))
- {
- if (m_config.iRcvTimeOut >= 0) // otherwise it's "no timeout set"
- timeout = true;
- HLOGP(tslog.Debug,
- "receiveMessage: DATA COND: EXPIRED -- checking connection conditions and rolling again");
- }
- else
- {
- HLOGP(tslog.Debug, "receiveMessage: DATA COND: KICKED.");
- }
- } while (stillConnected() && !timeout && (!isRcvBufferReady()));
- THREAD_RESUMED();
- HLOGC(tslog.Debug,
- log << CONID() << "receiveMessage: lock-waiting loop exited: stillConntected=" << stillConnected()
- << " timeout=" << timeout << " data-ready=" << isRcvBufferReady());
- }
- /* XXX DEBUG STUFF - enable when required
- LOGC(arlog.Debug, "RECVMSG/GO-ON BROKEN " << m_bBroken << " CONN " << m_bConnected
- << " CLOSING " << m_bClosing << " TMOUT " << timeout
- << " NMSG " << m_pRcvBuffer->getRcvMsgNum());
- */
- enterCS(m_RcvBufferLock);
- res = m_pRcvBuffer->readMessage((data), len, &w_mctrl);
- leaveCS(m_RcvBufferLock);
- HLOGC(arlog.Debug, log << CONID() << "AFTER readMsg: (BLOCKING) result=" << res);
- if (m_bBroken || m_bClosing)
- {
- // Forced to return 0 instead of throwing exception.
- if (!by_exception)
- return APIError(MJ_CONNECTION, MN_CONNLOST, 0);
- if (!m_config.bMessageAPI && m_bShutdown)
- return 0;
- throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
- }
- else if (!m_bConnected)
- {
- // Forced to return -1 instead of throwing exception.
- if (!by_exception)
- return APIError(MJ_CONNECTION, MN_NOCONN, 0);
- throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
- }
- } while ((res == 0) && !timeout);
- if (!isRcvBufferReady())
- {
- // Falling here means usually that res == 0 && timeout == true.
- // res == 0 would repeat the above loop, unless there was also a timeout.
- // timeout has interrupted the above loop, but with res > 0 this condition
- // wouldn't be satisfied.
- // read is not available any more
- // Kick TsbPd thread to schedule next wakeup (if running)
- if (m_bTsbPd)
- {
- HLOGP(tslog.Debug, "recvmsg: KICK tsbpd() (buffer empty)");
- tscond.notify_one_locked(recvguard);
- }
- // Shut up EPoll if no more messages in non-blocking mode
- uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false);
- }
- // Unblock when required
- // LOGC(tslog.Debug, "RECVMSG/EXIT RES " << res << " RCVTIMEOUT");
- if ((res <= 0) && (m_config.iRcvTimeOut >= 0))
- {
- // Forced to return -1 instead of throwing exception.
- if (!by_exception)
- return APIError(MJ_AGAIN, MN_XMTIMEOUT, 0);
- throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0);
- }
- return res;
- }
- int64_t srt::CUDT::sendfile(fstream &ifs, int64_t &offset, int64_t size, int block)
- {
- if (m_bBroken || m_bClosing)
- throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
- else if (!m_bConnected || !m_CongCtl.ready())
- throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
- if (size <= 0 && size != -1)
- return 0;
- if (!m_CongCtl->checkTransArgs(SrtCongestion::STA_FILE, SrtCongestion::STAD_SEND, 0, size, SRT_MSGTTL_INF, false))
- throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0);
- if (!m_pCryptoControl || !m_pCryptoControl->isSndEncryptionOK())
- {
- LOGC(aslog.Error,
- log << CONID()
- << "Encryption is required, but the peer did not supply correct credentials. Sending rejected.");
- throw CUDTException(MJ_SETUP, MN_SECURITY, 0);
- }
- ScopedLock sendguard (m_SendLock);
- if (m_pSndBuffer->getCurrBufSize() == 0)
- {
- // delay the EXP timer to avoid mis-fired timeout
- ScopedLock ack_lock(m_RecvAckLock);
- m_tsLastRspAckTime = steady_clock::now();
- m_iReXmitCount = 1;
- }
- // positioning...
- try
- {
- if (size == -1)
- {
- ifs.seekg(0, std::ios::end);
- size = ifs.tellg();
- if (offset > size)
- throw 0; // let it be caught below
- }
- // This will also set the position back to the beginning
- // in case when it was moved to the end for measuring the size.
- // This will also fail if the offset exceeds size, so measuring
- // the size can be skipped if not needed.
- ifs.seekg((streamoff)offset);
- if (!ifs.good())
- throw 0;
- }
- catch (...)
- {
- // XXX It would be nice to note that this is reported
- // by exception only if explicitly requested by setting
- // the exception flags in the stream. Here it's fixed so
- // that when this isn't set, the exception is "thrown manually".
- throw CUDTException(MJ_FILESYSTEM, MN_SEEKGFAIL);
- }
- int64_t tosend = size;
- int unitsize;
- // sending block by block
- while (tosend > 0)
- {
- if (ifs.fail())
- throw CUDTException(MJ_FILESYSTEM, MN_WRITEFAIL);
- if (ifs.eof())
- break;
- unitsize = int((tosend >= block) ? block : tosend);
- {
- UniqueLock lock(m_SendBlockLock);
- THREAD_PAUSED();
- while (stillConnected() && (sndBuffersLeft() <= 0) && m_bPeerHealth)
- m_SendBlockCond.wait(lock);
- THREAD_RESUMED();
- }
- if (m_bBroken || m_bClosing)
- throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
- else if (!m_bConnected)
- throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
- else if (!m_bPeerHealth)
- {
- // reset peer health status, once this error returns, the app should handle the situation at the peer side
- m_bPeerHealth = true;
- throw CUDTException(MJ_PEERERROR);
- }
- // record total time used for sending
- if (m_pSndBuffer->getCurrBufSize() == 0)
- {
- ScopedLock lock(m_StatsLock);
- m_stats.sndDurationCounter = steady_clock::now();
- }
- {
- ScopedLock recvAckLock(m_RecvAckLock);
- const int64_t sentsize = m_pSndBuffer->addBufferFromFile(ifs, unitsize);
- if (sentsize > 0)
- {
- tosend -= sentsize;
- offset += sentsize;
- }
- if (sndBuffersLeft() <= 0)
- {
- // write is not available any more
- uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, false);
- }
- }
- // insert this socket to snd list if it is not on the list yet
- m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE);
- }
- return size - tosend;
- }
- int64_t srt::CUDT::recvfile(fstream &ofs, int64_t &offset, int64_t size, int block)
- {
- if (!m_bConnected || !m_CongCtl.ready())
- throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
- else if ((m_bBroken || m_bClosing) && !isRcvBufferReady())
- {
- if (!m_config.bMessageAPI && m_bShutdown)
- return 0;
- throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
- }
- if (size <= 0)
- return 0;
- if (!m_CongCtl->checkTransArgs(SrtCongestion::STA_FILE, SrtCongestion::STAD_RECV, 0, size, SRT_MSGTTL_INF, false))
- throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0);
- if (isOPT_TsbPd())
- {
- LOGC(arlog.Error,
- log << CONID() << "Reading from file is incompatible with TSBPD mode and would cause a deadlock");
- throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0);
- }
- UniqueLock recvguard(m_RecvLock);
- // Well, actually as this works over a FILE (fstream), not just a stream,
- // the size can be measured anyway and predicted if setting the offset might
- // have a chance to work or not.
- // positioning...
- try
- {
- if (offset > 0)
- {
- // Don't do anything around here if the offset == 0, as this
- // is the default offset after opening. Whether this operation
- // is performed correctly, it highly depends on how the file
- // has been open. For example, if you want to overwrite parts
- // of an existing file, the file must exist, and the ios::trunc
- // flag must not be set. If the file is open for only ios::out,
- // then the file will be truncated since the offset position on
- // at the time when first written; if ios::in|ios::out, then
- // it won't be truncated, just overwritten.
- // What is required here is that if offset is 0, don't try to
- // change the offset because this might be impossible with
- // the current flag set anyway.
- // Also check the status and CAUSE exception manually because
- // you don't know, as well, whether the user has set exception
- // flags.
- ofs.seekp((streamoff)offset);
- if (!ofs.good())
- throw 0; // just to get caught :)
- }
- }
- catch (...)
- {
- // XXX It would be nice to note that this is reported
- // by exception only if explicitly requested by setting
- // the exception flags in the stream. For a case, when it's not,
- // an additional explicit throwing happens when failbit is set.
- throw CUDTException(MJ_FILESYSTEM, MN_SEEKPFAIL);
- }
- int64_t torecv = size;
- int unitsize = block;
- int recvsize;
- // receiving... "recvfile" is always blocking
- while (torecv > 0)
- {
- if (ofs.fail())
- {
- // send the sender a signal so it will not be blocked forever
- int32_t err_code = CUDTException::EFILE;
- sendCtrl(UMSG_PEERERROR, &err_code);
- throw CUDTException(MJ_FILESYSTEM, MN_WRITEFAIL);
- }
- {
- CSync rcond (m_RecvDataCond, recvguard);
- THREAD_PAUSED();
- while (stillConnected() && !isRcvBufferReady())
- rcond.wait();
- THREAD_RESUMED();
- }
- if (!m_bConnected)
- throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
- else if ((m_bBroken || m_bClosing) && !isRcvBufferReady())
- {
- if (!m_config.bMessageAPI && m_bShutdown)
- return 0;
- throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
- }
- unitsize = int((torecv > block) ? block : torecv);
- enterCS(m_RcvBufferLock);
- recvsize = m_pRcvBuffer->readBufferToFile(ofs, unitsize);
- leaveCS(m_RcvBufferLock);
- if (recvsize > 0)
- {
- torecv -= recvsize;
- offset += recvsize;
- }
- }
- if (!isRcvBufferReady())
- {
- // read is not available any more
- uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false);
- }
- return size - torecv;
- }
- void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous)
- {
- if (!m_bConnected)
- throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
- if (m_bBroken || m_bClosing)
- throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0);
- const int pktHdrSize = CPacket::HDR_SIZE + CPacket::UDP_HDR_SIZE;
- {
- ScopedLock statsguard(m_StatsLock);
- const steady_clock::time_point currtime = steady_clock::now();
- perf->msTimeStamp = count_milliseconds(currtime - m_stats.tsStartTime);
- perf->pktSent = m_stats.sndr.sent.trace.count();
- perf->pktSentUnique = m_stats.sndr.sentUnique.trace.count();
- perf->pktRecv = m_stats.rcvr.recvd.trace.count();
- perf->pktRecvUnique = m_stats.rcvr.recvdUnique.trace.count();
- perf->pktSndLoss = m_stats.sndr.lost.trace.count();
- perf->pktRcvLoss = m_stats.rcvr.lost.trace.count();
- perf->pktRetrans = m_stats.sndr.sentRetrans.trace.count();
- perf->pktRcvRetrans = m_stats.rcvr.recvdRetrans.trace.count();
- perf->pktSentACK = m_stats.rcvr.sentAck.trace.count();
- perf->pktRecvACK = m_stats.sndr.recvdAck.trace.count();
- perf->pktSentNAK = m_stats.rcvr.sentNak.trace.count();
- perf->pktRecvNAK = m_stats.sndr.recvdNak.trace.count();
- perf->usSndDuration = m_stats.sndDuration;
- perf->pktReorderDistance = m_stats.traceReorderDistance;
- perf->pktReorderTolerance = m_iReorderTolerance;
- perf->pktRcvAvgBelatedTime = m_stats.traceBelatedTime;
- perf->pktRcvBelated = m_stats.rcvr.recvdBelated.trace.count();
- perf->pktSndFilterExtra = m_stats.sndr.sentFilterExtra.trace.count();
- perf->pktRcvFilterExtra = m_stats.rcvr.recvdFilterExtra.trace.count();
- perf->pktRcvFilterSupply = m_stats.rcvr.suppliedByFilter.trace.count();
- perf->pktRcvFilterLoss = m_stats.rcvr.lossFilter.trace.count();
- /* perf byte counters include all headers (SRT+UDP+IP) */
- perf->byteSent = m_stats.sndr.sent.trace.bytesWithHdr();
- perf->byteSentUnique = m_stats.sndr.sentUnique.trace.bytesWithHdr();
- perf->byteRecv = m_stats.rcvr.recvd.trace.bytesWithHdr();
- perf->byteRecvUnique = m_stats.rcvr.recvdUnique.trace.bytesWithHdr();
- perf->byteRetrans = m_stats.sndr.sentRetrans.trace.bytesWithHdr();
- perf->byteRcvLoss = m_stats.rcvr.lost.trace.bytesWithHdr();
- perf->pktSndDrop = m_stats.sndr.dropped.trace.count();
- perf->pktRcvDrop = m_stats.rcvr.dropped.trace.count();
- perf->byteSndDrop = m_stats.sndr.dropped.trace.bytesWithHdr();
- perf->byteRcvDrop = m_stats.rcvr.dropped.trace.bytesWithHdr();
- perf->pktRcvUndecrypt = m_stats.rcvr.undecrypted.trace.count();
- perf->byteRcvUndecrypt = m_stats.rcvr.undecrypted.trace.bytes();
- perf->pktSentTotal = m_stats.sndr.sent.total.count();
- perf->pktSentUniqueTotal = m_stats.sndr.sentUnique.total.count();
- perf->pktRecvTotal = m_stats.rcvr.recvd.total.count();
- perf->pktRecvUniqueTotal = m_stats.rcvr.recvdUnique.total.count();
- perf->pktSndLossTotal = m_stats.sndr.lost.total.count();
- perf->pktRcvLossTotal = m_stats.rcvr.lost.total.count();
- perf->pktRetransTotal = m_stats.sndr.sentRetrans.total.count();
- perf->pktSentACKTotal = m_stats.rcvr.sentAck.total.count();
- perf->pktRecvACKTotal = m_stats.sndr.recvdAck.total.count();
- perf->pktSentNAKTotal = m_stats.rcvr.sentNak.total.count();
- perf->pktRecvNAKTotal = m_stats.sndr.recvdNak.total.count();
- perf->usSndDurationTotal = m_stats.m_sndDurationTotal;
- perf->byteSentTotal = m_stats.sndr.sent.total.bytesWithHdr();
- perf->byteSentUniqueTotal = m_stats.sndr.sentUnique.total.bytesWithHdr();
- perf->byteRecvTotal = m_stats.rcvr.recvd.total.bytesWithHdr();
- perf->byteRecvUniqueTotal = m_stats.rcvr.recvdUnique.total.bytesWithHdr();
- perf->byteRetransTotal = m_stats.sndr.sentRetrans.total.bytesWithHdr();
- perf->pktSndFilterExtraTotal = m_stats.sndr.sentFilterExtra.total.count();
- perf->pktRcvFilterExtraTotal = m_stats.rcvr.recvdFilterExtra.total.count();
- perf->pktRcvFilterSupplyTotal = m_stats.rcvr.suppliedByFilter.total.count();
- perf->pktRcvFilterLossTotal = m_stats.rcvr.lossFilter.total.count();
- perf->byteRcvLossTotal = m_stats.rcvr.lost.total.bytesWithHdr();
- perf->pktSndDropTotal = m_stats.sndr.dropped.total.count();
- perf->pktRcvDropTotal = m_stats.rcvr.dropped.total.count();
- // TODO: The payload is dropped. Probably header sizes should not be counted?
- perf->byteSndDropTotal = m_stats.sndr.dropped.total.bytesWithHdr();
- perf->byteRcvDropTotal = m_stats.rcvr.dropped.total.bytesWithHdr();
- perf->pktRcvUndecryptTotal = m_stats.rcvr.undecrypted.total.count();
- perf->byteRcvUndecryptTotal = m_stats.rcvr.undecrypted.total.bytes();
- // TODO: The following class members must be protected with a different mutex, not the m_StatsLock.
- const double interval = (double) count_microseconds(currtime - m_stats.tsLastSampleTime);
- perf->mbpsSendRate = double(perf->byteSent) * 8.0 / interval;
- perf->mbpsRecvRate = double(perf->byteRecv) * 8.0 / interval;
- perf->usPktSndPeriod = (double) count_microseconds(m_tdSendInterval.load());
- perf->pktFlowWindow = m_iFlowWindowSize.load();
- perf->pktCongestionWindow = (int)m_dCongestionWindow;
- perf->pktFlightSize = getFlightSpan();
- perf->msRTT = (double)m_iSRTT / 1000.0;
- perf->msSndTsbPdDelay = m_bPeerTsbPd ? m_iPeerTsbPdDelay_ms : 0;
- perf->msRcvTsbPdDelay = isOPT_TsbPd() ? m_iTsbPdDelay_ms : 0;
- perf->byteMSS = m_config.iMSS;
- perf->mbpsMaxBW = m_config.llMaxBW > 0 ? Bps2Mbps(m_config.llMaxBW)
- : m_CongCtl.ready() ? Bps2Mbps(m_CongCtl->sndBandwidth())
- : 0;
- if (clear)
- {
- m_stats.sndr.resetTrace();
- m_stats.rcvr.resetTrace();
- m_stats.sndDuration = 0;
- m_stats.tsLastSampleTime = currtime;
- }
- }
- const int64_t availbw = m_iBandwidth == 1 ? m_RcvTimeWindow.getBandwidth() : m_iBandwidth.load();
- perf->mbpsBandwidth = Bps2Mbps(availbw * (m_iMaxSRTPayloadSize + pktHdrSize));
- if (tryEnterCS(m_ConnectionLock))
- {
- if (m_pSndBuffer)
- {
- if (instantaneous)
- {
- /* Get instant SndBuf instead of moving average for application-based Algorithm
- (such as NAE) in need of fast reaction to network condition changes. */
- perf->pktSndBuf = m_pSndBuffer->getCurrBufSize((perf->byteSndBuf), (perf->msSndBuf));
- }
- else
- {
- perf->pktSndBuf = m_pSndBuffer->getAvgBufSize((perf->byteSndBuf), (perf->msSndBuf));
- }
- perf->byteSndBuf += (perf->pktSndBuf * pktHdrSize);
- perf->byteAvailSndBuf = (m_config.iSndBufSize - perf->pktSndBuf) * m_config.iMSS;
- }
- else
- {
- perf->byteAvailSndBuf = 0;
- perf->pktSndBuf = 0;
- perf->byteSndBuf = 0;
- perf->msSndBuf = 0;
- }
- if (m_pRcvBuffer)
- {
- ScopedLock lck(m_RcvBufferLock);
- perf->byteAvailRcvBuf = (int) getAvailRcvBufferSizeNoLock() * m_config.iMSS;
- if (instantaneous) // no need for historical API for Rcv side
- {
- perf->pktRcvBuf = m_pRcvBuffer->getRcvDataSize(perf->byteRcvBuf, perf->msRcvBuf);
- }
- else
- {
- perf->pktRcvBuf = m_pRcvBuffer->getRcvAvgDataSize(perf->byteRcvBuf, perf->msRcvBuf);
- }
- }
- else
- {
- perf->byteAvailRcvBuf = 0;
- perf->pktRcvBuf = 0;
- perf->byteRcvBuf = 0;
- perf->msRcvBuf = 0;
- }
- leaveCS(m_ConnectionLock);
- }
- else
- {
- perf->byteAvailSndBuf = 0;
- perf->byteAvailRcvBuf = 0;
- perf->pktSndBuf = 0;
- perf->byteSndBuf = 0;
- perf->msSndBuf = 0;
- perf->byteRcvBuf = 0;
- perf->msRcvBuf = 0;
- }
- }
- bool srt::CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg)
- {
- // Special things that must be done HERE, not in SrtCongestion,
- // because it involves the input buffer in CUDT. It would be
- // slightly dangerous to give SrtCongestion access to it.
- // According to the rules, the congctl should be ready at the same
- // time when the sending buffer. For sanity check, check both first.
- if (!m_CongCtl.ready() || !m_pSndBuffer)
- {
- LOGC(rslog.Error,
- log << CONID() << "updateCC: CAN'T DO UPDATE - congctl " << (m_CongCtl.ready() ? "ready" : "NOT READY")
- << "; sending buffer " << (m_pSndBuffer ? "NOT CREATED" : "created"));
- return false;
- }
- HLOGC(rslog.Debug, log << CONID() << "updateCC: EVENT:" << TransmissionEventStr(evt));
- if (evt == TEV_INIT)
- {
- // only_input uses:
- // 0: in the beginning and when SRTO_MAXBW was changed
- // 1: SRTO_INPUTBW was changed
- // 2: SRTO_OHEADBW was changed
- EInitEvent only_input = arg.get<EventVariant::INIT>();
- // false = TEV_INIT_RESET: in the beginning, or when MAXBW was changed.
- if (only_input != TEV_INIT_RESET && m_config.llMaxBW)
- {
- HLOGC(rslog.Debug, log << CONID() << "updateCC/TEV_INIT: non-RESET stage and m_config.llMaxBW already set to " << m_config.llMaxBW);
- // Don't change
- }
- else // either m_config.llMaxBW == 0 or only_input == TEV_INIT_RESET
- {
- // Use the values:
- // - if SRTO_MAXBW is >0, use it.
- // - if SRTO_MAXBW == 0, use SRTO_INPUTBW + SRTO_OHEADBW
- // - if SRTO_INPUTBW == 0, pass 0 to requst in-buffer sampling
- // Bytes/s
- const int64_t bw = m_config.llMaxBW != 0 ? m_config.llMaxBW : // When used SRTO_MAXBW
- m_config.llInputBW != 0 ? withOverhead(m_config.llInputBW) : // SRTO_INPUTBW + SRT_OHEADBW
- 0; // When both MAXBW and INPUTBW are 0, request in-buffer sampling
- // Note: setting bw == 0 uses BW_INFINITE value in LiveCC
- m_CongCtl->updateBandwidth(m_config.llMaxBW, bw);
- if (only_input == TEV_INIT_OHEADBW)
- {
- // On updated SRTO_OHEADBW don't change input rate.
- // This only influences the call to withOverhead().
- }
- else
- {
- // No need to calculate input rate if the bandwidth is set
- const bool disable_in_rate_calc = (bw != 0);
- m_pSndBuffer->resetInputRateSmpPeriod(disable_in_rate_calc);
- }
- HLOGC(rslog.Debug,
- log << CONID() << "updateCC/TEV_INIT: updating BW=" << m_config.llMaxBW
- << (only_input == TEV_INIT_RESET
- ? " (UNCHANGED)"
- : only_input == TEV_INIT_OHEADBW ? " (only Overhead)" : " (updated sampling rate)"));
- }
- }
- // This part is also required only by LiveCC, however not
- // moved there due to that it needs access to CSndBuffer.
- if (evt == TEV_ACK || evt == TEV_LOSSREPORT || evt == TEV_CHECKTIMER || evt == TEV_SYNC)
- {
- // Specific part done when MaxBW is set to 0 (auto) and InputBW is 0.
- // This requests internal input rate sampling.
- if (m_config.llMaxBW == 0 && m_config.llInputBW == 0)
- {
- // Get auto-calculated input rate, Bytes per second
- const int64_t inputbw = m_pSndBuffer->getInputRate();
- /*
- * On blocked transmitter (tx full) and until connection closes,
- * auto input rate falls to 0 but there may be still lot of packet to retransmit
- * Calling updateBandwidth with 0 sets maxBW to default BW_INFINITE (1 Gbps)
- * and sendrate skyrockets for retransmission.
- * Keep previously set maximum in that case (inputbw == 0).
- */
- if (inputbw >= 0)
- m_CongCtl->updateBandwidth(0, withOverhead(std::max(m_config.llMinInputBW, inputbw))); // Bytes/sec
- }
- }
- HLOGC(rslog.Debug, log << CONID() << "updateCC: emitting signal for EVENT:" << TransmissionEventStr(evt));
- // Now execute a congctl-defined action for that event.
- EmitSignal(evt, arg);
- // This should be done with every event except ACKACK and SEND/RECEIVE
- // After any action was done by the congctl, update the congestion window and sending interval.
- if (evt != TEV_ACKACK && evt != TEV_SEND && evt != TEV_RECEIVE)
- {
- // This part comes from original UDT.
- // NOTE: THESE things come from CCC class:
- // - m_dPktSndPeriod
- // - m_dCWndSize
- m_tdSendInterval = microseconds_from((int64_t)m_CongCtl->pktSndPeriod_us());
- m_dCongestionWindow = m_CongCtl->cgWindowSize();
- #if ENABLE_HEAVY_LOGGING
- HLOGC(rslog.Debug,
- log << CONID() << "updateCC: updated values from congctl: interval=" << count_microseconds(m_tdSendInterval) << " us ("
- << "tk (" << m_CongCtl->pktSndPeriod_us() << "us) cgwindow="
- << std::setprecision(3) << m_dCongestionWindow);
- #endif
- }
- HLOGC(rslog.Debug, log << CONID() << "udpateCC: finished handling for EVENT:" << TransmissionEventStr(evt));
- return true;
- }
- void srt::CUDT::initSynch()
- {
- setupMutex(m_SendBlockLock, "SendBlock");
- setupCond(m_SendBlockCond, "SendBlock");
- setupCond(m_RecvDataCond, "RecvData");
- setupMutex(m_SendLock, "Send");
- setupMutex(m_RecvLock, "Recv");
- setupMutex(m_RcvLossLock, "RcvLoss");
- setupMutex(m_RecvAckLock, "RecvAck");
- setupMutex(m_RcvBufferLock, "RcvBuffer");
- setupMutex(m_ConnectionLock, "Connection");
- setupMutex(m_StatsLock, "Stats");
- setupCond(m_RcvTsbPdCond, "RcvTsbPd");
- }
- void srt::CUDT::destroySynch()
- {
- releaseMutex(m_SendBlockLock);
- // Just in case, signal the CV, on which some
- // other thread is possibly waiting, because a
- // process hanging on a pthread_cond_wait would
- // cause the call to destroy a CV hang up.
- m_SendBlockCond.notify_all();
- releaseCond(m_SendBlockCond);
- m_RecvDataCond.notify_all();
- releaseCond(m_RecvDataCond);
- releaseMutex(m_SendLock);
- releaseMutex(m_RecvLock);
- releaseMutex(m_RcvLossLock);
- releaseMutex(m_RecvAckLock);
- releaseMutex(m_RcvBufferLock);
- releaseMutex(m_ConnectionLock);
- releaseMutex(m_StatsLock);
- m_RcvTsbPdCond.notify_all();
- releaseCond(m_RcvTsbPdCond);
- }
- void srt::CUDT::releaseSynch()
- {
- SRT_ASSERT(m_bClosing);
- // wake up user calls
- CSync::lock_notify_one(m_SendBlockCond, m_SendBlockLock);
- enterCS(m_SendLock);
- leaveCS(m_SendLock);
- // Awake tsbpd() and srt_recv*(..) threads for them to check m_bClosing.
- CSync::lock_notify_one(m_RecvDataCond, m_RecvLock);
- CSync::lock_notify_one(m_RcvTsbPdCond, m_RecvLock);
- // Azquiring m_RcvTsbPdStartupLock protects race in starting
- // the tsbpd() thread in CUDT::processData().
- // Wait for tsbpd() thread to finish.
- enterCS(m_RcvTsbPdStartupLock);
- if (m_RcvTsbPdThread.joinable())
- {
- m_RcvTsbPdThread.join();
- }
- leaveCS(m_RcvTsbPdStartupLock);
- // Acquiring the m_RecvLock it is assumed that both tsbpd()
- // and srt_recv*(..) threads will be aware about the state of m_bClosing.
- enterCS(m_RecvLock);
- leaveCS(m_RecvLock);
- }
- #if ENABLE_BONDING
- void srt::CUDT::dropToGroupRecvBase()
- {
- int32_t group_recv_base = SRT_SEQNO_NONE;
- if (m_parent->m_GroupOf)
- {
- // Check is first done before locking to avoid unnecessary
- // mutex locking. The condition for this field is that it
- // can be either never set, already reset, or ever set
- // and possibly dangling. The re-check after lock eliminates
- // the dangling case.
- ScopedLock glock (uglobal().m_GlobControlLock);
- // Note that getRcvBaseSeqNo() will lock m_GroupOf->m_GroupLock,
- // but this is an intended order.
- if (m_parent->m_GroupOf)
- group_recv_base = m_parent->m_GroupOf->getRcvBaseSeqNo();
- }
- if (group_recv_base == SRT_SEQNO_NONE)
- return;
- ScopedLock lck(m_RcvBufferLock);
- int cnt = rcvDropTooLateUpTo(CSeqNo::incseq(group_recv_base));
- if (cnt > 0)
- {
- HLOGC(grlog.Debug,
- log << CONID() << "dropToGroupRecvBase: dropped " << cnt << " packets before ACK: group_recv_base="
- << group_recv_base << " m_iRcvLastAck=" << m_iRcvLastAck
- << " m_iRcvCurrSeqNo=" << m_iRcvCurrSeqNo << " m_bTsbPd=" << m_bTsbPd);
- }
- }
- #endif
- namespace srt {
- #if ENABLE_HEAVY_LOGGING
- static void DebugAck(string hdr, int prev, int ack)
- {
- if (!prev)
- {
- HLOGC(xtlog.Debug, log << hdr << "ACK " << ack);
- return;
- }
- int diff = CSeqNo::seqoff(prev, ack);
- if (diff < 0)
- {
- HLOGC(xtlog.Debug, log << hdr << "ACK ERROR: " << prev << "-" << ack << "(diff " << diff << ")");
- return;
- }
- bool shorted = diff > 100; // sanity
- if (shorted)
- ack = CSeqNo::incseq(prev, 100);
- ostringstream ackv;
- for (; prev != ack; prev = CSeqNo::incseq(prev))
- ackv << prev << " ";
- if (shorted)
- ackv << "...";
- HLOGC(xtlog.Debug, log << hdr << "ACK (" << (diff + 1) << "): " << ackv.str() << ack);
- }
- #else
- static inline void DebugAck(string, int, int) {}
- #endif
- }
- void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rparam, int size)
- {
- CPacket ctrlpkt;
- setPacketTS(ctrlpkt, steady_clock::now());
- int nbsent = 0;
- switch (pkttype)
- {
- case UMSG_ACK: // 010 - Acknowledgement
- {
- nbsent = sendCtrlAck(ctrlpkt, size);
- break;
- }
- case UMSG_ACKACK: // 110 - Acknowledgement of Acknowledgement
- ctrlpkt.pack(pkttype, lparam);
- ctrlpkt.m_iID = m_PeerID;
- nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr);
- break;
- case UMSG_LOSSREPORT: // 011 - Loss Report
- {
- // Explicitly defined lost sequences
- if (rparam)
- {
- int32_t *lossdata = (int32_t *)rparam;
- size_t bytes = sizeof(*lossdata) * size;
- ctrlpkt.pack(pkttype, NULL, lossdata, bytes);
- ctrlpkt.m_iID = m_PeerID;
- nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr);
- enterCS(m_StatsLock);
- m_stats.rcvr.sentNak.count(1);
- leaveCS(m_StatsLock);
- }
- // Call with no arguments - get loss list from internal data.
- else if (m_pRcvLossList->getLossLength() > 0)
- {
- ScopedLock lock(m_RcvLossLock);
- // this is periodically NAK report; make sure NAK cannot be sent back too often
- // read loss list from the local receiver loss list
- int32_t *data = new int32_t[m_iMaxSRTPayloadSize / 4];
- int losslen;
- m_pRcvLossList->getLossArray(data, losslen, m_iMaxSRTPayloadSize / 4);
- if (0 < losslen)
- {
- ctrlpkt.pack(pkttype, NULL, data, losslen * 4);
- ctrlpkt.m_iID = m_PeerID;
- nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr);
- enterCS(m_StatsLock);
- m_stats.rcvr.sentNak.count(1);
- leaveCS(m_StatsLock);
- }
- delete[] data;
- }
- // update next NAK time, which should wait enough time for the retansmission, but not too long
- m_tdNAKInterval = microseconds_from(m_iSRTT + 4 * m_iRTTVar);
- // Fix the NAKreport period according to the congctl
- m_tdNAKInterval =
- microseconds_from(m_CongCtl->updateNAKInterval(count_microseconds(m_tdNAKInterval),
- m_RcvTimeWindow.getPktRcvSpeed(),
- m_pRcvLossList->getLossLength()));
- // This is necessary because a congctl need not wish to define
- // its own minimum interval, in which case the default one is used.
- if (m_tdNAKInterval < m_tdMinNakInterval)
- m_tdNAKInterval = m_tdMinNakInterval;
- break;
- }
- case UMSG_CGWARNING: // 100 - Congestion Warning
- ctrlpkt.pack(pkttype);
- ctrlpkt.m_iID = m_PeerID;
- nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr);
- m_tsLastWarningTime = steady_clock::now();
- break;
- case UMSG_KEEPALIVE: // 001 - Keep-alive
- ctrlpkt.pack(pkttype);
- ctrlpkt.m_iID = m_PeerID;
- nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr);
- break;
- case UMSG_HANDSHAKE: // 000 - Handshake
- ctrlpkt.pack(pkttype, NULL, rparam, sizeof(CHandShake));
- ctrlpkt.m_iID = m_PeerID;
- nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr);
- break;
- case UMSG_SHUTDOWN: // 101 - Shutdown
- if (m_PeerID == 0) // Dont't send SHUTDOWN if we don't know peer ID.
- break;
- ctrlpkt.pack(pkttype);
- ctrlpkt.m_iID = m_PeerID;
- nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr);
- break;
- case UMSG_DROPREQ: // 111 - Msg drop request
- ctrlpkt.pack(pkttype, lparam, rparam, 8);
- ctrlpkt.m_iID = m_PeerID;
- nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr);
- break;
- case UMSG_PEERERROR: // 1000 - acknowledge the peer side a special error
- ctrlpkt.pack(pkttype, lparam);
- ctrlpkt.m_iID = m_PeerID;
- nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr);
- break;
- case UMSG_EXT: // 0x7FFF - Resevered for future use
- break;
- default:
- break;
- }
- // Fix keepalive
- if (nbsent)
- m_tsLastSndTime.store(steady_clock::now());
- }
- // [[using locked(m_RcvBufferLock)]]
- bool srt::CUDT::getFirstNoncontSequence(int32_t& w_seq, string& w_log_reason)
- {
- {
- ScopedLock losslock (m_RcvLossLock);
- const int32_t seq = m_pRcvLossList->getFirstLostSeq();
- if (seq != SRT_SEQNO_NONE)
- {
- HLOGC(xtlog.Debug, log << "NONCONT-SEQUENCE: first loss %" << seq << " (loss len=" <<
- m_pRcvLossList->getLossLength() << ")");
- w_seq = seq;
- w_log_reason = "first lost";
- return true;
- }
- }
- w_seq = CSeqNo::incseq(m_iRcvCurrSeqNo);
- HLOGC(xtlog.Debug, log << "NONCONT-SEQUENCE: past-recv %" << w_seq);
- w_log_reason = "expected next";
- return true;
- }
- int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size)
- {
- SRT_ASSERT(ctrlpkt.getMsgTimeStamp() != 0);
- int nbsent = 0;
- int local_prevack = 0;
- #if ENABLE_HEAVY_LOGGING
- struct SaveBack
- {
- int& target;
- const int& source;
- ~SaveBack() { target = source; }
- } l_saveback = { m_iDebugPrevLastAck, m_iRcvLastAck };
- (void)l_saveback; // kill compiler warning: unused variable `l_saveback` [-Wunused-variable]
- local_prevack = m_iDebugPrevLastAck;
- #endif
- string reason; // just for "a reason" of giving particular % for ACK
- #if ENABLE_BONDING
- dropToGroupRecvBase();
- #endif
- // The TSBPD thread may change the first lost sequence record (TLPKTDROP).
- // To avoid it the m_RcvBufferLock has to be acquired.
- UniqueLock bufflock(m_RcvBufferLock);
- // The full ACK should be sent to indicate there is now available space in the RCV buffer
- // since the last full ACK. It should unblock the sender to proceed further.
- const bool bNeedFullAck = (m_bBufferWasFull && getAvailRcvBufferSizeNoLock() > 0);
- int32_t ack; // First unacknowledged packet sequence number (acknowledge up to ack).
- if (!getFirstNoncontSequence((ack), (reason)))
- return nbsent;
- if (m_iRcvLastAckAck == ack && !bNeedFullAck)
- {
- HLOGC(xtlog.Debug,
- log << CONID() << "sendCtrl(UMSG_ACK): last ACK %" << ack << "(" << reason << ") == last ACKACK");
- return nbsent;
- }
- // send out a lite ACK
- // to save time on buffer processing and bandwidth/AS measurement, a lite ACK only feeds back an ACK number
- if (size == SEND_LITE_ACK && !bNeedFullAck)
- {
- bufflock.unlock();
- ctrlpkt.pack(UMSG_ACK, NULL, &ack, size);
- ctrlpkt.m_iID = m_PeerID;
- nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr);
- DebugAck(CONID() + "sendCtrl(lite): ", local_prevack, ack);
- return nbsent;
- }
- // IF ack %> m_iRcvLastAck
- // There are new received packets to acknowledge, update related information.
- if (CSeqNo::seqcmp(ack, m_iRcvLastAck) > 0)
- {
- // Sanity check if the "selected ACK" points to a sequence
- // in the past for the buffer. This SHOULD NEVER HAPPEN because
- // on drop the loss records should have been removed, and the last received
- // sequence also can't be in the past towards the buffer.
- // NOTE: This problem has been observed when the packet sequence
- // was incorrectly removed from the receiver loss list. This should
- // then stay here as a condition in order to detect this problem,
- // should it happen in the future.
- if (CSeqNo::seqcmp(ack, m_pRcvBuffer->getStartSeqNo()) < 0)
- {
- LOGC(xtlog.Error,
- log << CONID() << "sendCtrlAck: IPE: invalid ACK from %" << m_iRcvLastAck << " to %" << ack << " ("
- << CSeqNo::seqoff(m_iRcvLastAck, ack) << " packets) buffer=%" << m_pRcvBuffer->getStartSeqNo());
- }
- else
- {
- HLOGC(xtlog.Debug,
- log << CONID() << "sendCtrlAck: %" << m_iRcvLastAck << " -> %" << ack << " ("
- << CSeqNo::seqoff(m_iRcvLastAck, ack) << " packets)");
- }
- m_iRcvLastAck = ack;
- #if ENABLE_BONDING
- const int32_t group_read_seq = m_pRcvBuffer->getFirstReadablePacketInfo(steady_clock::now()).seqno;
- #endif
- InvertedLock un_bufflock (m_RcvBufferLock);
- #if ENABLE_BONDING
- // This actually should be done immediately after the ACK pointers were
- // updated in this socket, but it can't be done inside this function due
- // to being run under a lock.
- // At this moment no locks are applied. The only lock used so far
- // was m_RcvBufferLock, but this was lifed above. At this moment
- // it is safe to apply any locks here. This function is affined
- // to CRcvQueue::worker thread, so it is free to apply locks as
- // required in the defined order. At present we only need the lock
- // on m_GlobControlLock to prevent the group from being deleted
- // in the meantime
- if (m_parent->m_GroupOf)
- {
- // Check is first done before locking to avoid unnecessary
- // mutex locking. The condition for this field is that it
- // can be either never set, already reset, or ever set
- // and possibly dangling. The re-check after lock eliminates
- // the dangling case.
- ScopedLock glock (uglobal().m_GlobControlLock);
- // Note that updateLatestRcv will lock m_GroupOf->m_GroupLock,
- // but this is an intended order.
- if (m_parent->m_GroupOf)
- {
- // A group may need to update the parallelly used idle links,
- // should it have any. Pass the current socket position in order
- // to skip it from the group loop.
- m_parent->m_GroupOf->updateLatestRcv(m_parent);
- }
- }
- #endif
- // If TSBPD is enabled, then INSTEAD OF signaling m_RecvDataCond,
- // signal m_RcvTsbPdCond. This will kick in the tsbpd thread, which
- // will signal m_RecvDataCond when there's time to play for particular
- // data packet.
- HLOGC(xtlog.Debug,
- log << CONID() << "ACK: clip %" << m_iRcvLastAck << "-%" << ack << ", REVOKED "
- << CSeqNo::seqoff(ack, m_iRcvLastAck) << " from RCV buffer");
- if (m_bTsbPd)
- {
- /* Newly acknowledged data, signal TsbPD thread */
- CUniqueSync tslcc (m_RecvLock, m_RcvTsbPdCond);
- // m_bTsbPdAckWakeup is protected by m_RecvLock in the tsbpd() thread
- if (m_bTsbPdAckWakeup)
- tslcc.notify_one();
- }
- else
- {
- {
- CUniqueSync rdcc (m_RecvLock, m_RecvDataCond);
- // Locks m_RcvBufferLock, which is unlocked above by InvertedLock un_bufflock.
- // Must check read-readiness under m_RecvLock to protect the epoll from concurrent changes in readBuffer()
- if (isRcvBufferReady())
- {
- if (m_config.bSynRecving)
- {
- // signal a waiting "recv" call if there is any data available
- rdcc.notify_one();
- }
- // acknowledge any waiting epolls to read
- // fix SRT_EPOLL_IN event loss but rcvbuffer still have data:
- // 1. user call receive/receivemessage(about line number:6482)
- // 2. after read/receive, if rcvbuffer is empty, will set SRT_EPOLL_IN event to false
- // 3. but if we do not do some lock work here, will cause some sync problems between threads:
- // (1) user thread: call receive/receivemessage
- // (2) user thread: read data
- // (3) user thread: no data in rcvbuffer, set SRT_EPOLL_IN event to false
- // (4) receive thread: receive data and set SRT_EPOLL_IN to true
- // (5) user thread: set SRT_EPOLL_IN to false
- // 4. so , m_RecvLock must be used here to protect epoll event
- uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, true);
- }
- }
- #if ENABLE_BONDING
- if (group_read_seq != SRT_SEQNO_NONE && m_parent->m_GroupOf)
- {
- // See above explanation for double-checking
- ScopedLock glock (uglobal().m_GlobControlLock);
- if (m_parent->m_GroupOf)
- {
- // The current "APP reader" needs to simply decide as to whether
- // the next CUDTGroup::recv() call should return with no blocking or not.
- // When the group is read-ready, it should update its pollers as it sees fit.
- m_parent->m_GroupOf->updateReadState(m_SocketID, group_read_seq);
- }
- }
- #endif
- CGlobEvent::triggerEvent();
- }
- }
- else if (ack == m_iRcvLastAck && !bNeedFullAck)
- {
- // If the ACK was just sent already AND elapsed time did not exceed RTT,
- if ((steady_clock::now() - m_tsLastAckTime) <
- (microseconds_from(m_iSRTT + 4 * m_iRTTVar)))
- {
- HLOGC(xtlog.Debug,
- log << CONID() << "sendCtrl(UMSG_ACK): ACK %" << ack << " just sent - too early to repeat");
- return nbsent;
- }
- }
- else if (!bNeedFullAck)
- {
- // Not possible (m_iRcvCurrSeqNo+1 <% m_iRcvLastAck ?)
- LOGC(xtlog.Error, log << CONID() << "sendCtrl(UMSG_ACK): IPE: curr %" << ack << " <% last %" << m_iRcvLastAck);
- return nbsent;
- }
- // [[using assert( ack >= m_iRcvLastAck && is_periodic_ack ) ]];
- // [[using locked(m_RcvBufferLock)]];
- // Send out the ACK only if has not been received by the sender before
- if (CSeqNo::seqcmp(m_iRcvLastAck, m_iRcvLastAckAck) > 0 || bNeedFullAck)
- {
- // NOTE: The BSTATS feature turns on extra fields above size 6
- // also known as ACKD_TOTAL_SIZE_VER100.
- int32_t data[ACKD_TOTAL_SIZE];
- // Case you care, CAckNo::incack does exactly the same thing as
- // CSeqNo::incseq. Logically the ACK number is a different thing
- // than sequence number (it's a "journal" for ACK request-response,
- // and starts from 0, unlike sequence, which starts from a random
- // number), but still the numbers are from exactly the same domain.
- m_iAckSeqNo = CAckNo::incack(m_iAckSeqNo);
- data[ACKD_RCVLASTACK] = m_iRcvLastAck;
- data[ACKD_RTT] = m_iSRTT;
- data[ACKD_RTTVAR] = m_iRTTVar;
- data[ACKD_BUFFERLEFT] = (int) getAvailRcvBufferSizeNoLock();
- m_bBufferWasFull = data[ACKD_BUFFERLEFT] == 0;
- if (steady_clock::now() - m_tsLastAckTime > m_tdACKInterval)
- {
- int rcvRate;
- int ctrlsz = ACKD_TOTAL_SIZE_UDTBASE * ACKD_FIELD_SIZE; // Minimum required size
- data[ACKD_RCVSPEED] = m_RcvTimeWindow.getPktRcvSpeed((rcvRate));
- data[ACKD_BANDWIDTH] = m_RcvTimeWindow.getBandwidth();
- //>>Patch while incompatible (1.0.2) receiver floating around
- if (m_uPeerSrtVersion == SrtVersion(1, 0, 2))
- {
- data[ACKD_RCVRATE] = rcvRate; // bytes/sec
- data[ACKD_XMRATE_VER102_ONLY] = data[ACKD_BANDWIDTH] * m_iMaxSRTPayloadSize; // bytes/sec
- ctrlsz = ACKD_FIELD_SIZE * ACKD_TOTAL_SIZE_VER102_ONLY;
- }
- else if (m_uPeerSrtVersion >= SrtVersion(1, 0, 3))
- {
- // Normal, currently expected version.
- data[ACKD_RCVRATE] = rcvRate; // bytes/sec
- ctrlsz = ACKD_FIELD_SIZE * ACKD_TOTAL_SIZE_VER101;
- }
- // ELSE: leave the buffer with ...UDTBASE size.
- ctrlpkt.pack(UMSG_ACK, &m_iAckSeqNo, data, ctrlsz);
- m_tsLastAckTime = steady_clock::now();
- }
- else
- {
- ctrlpkt.pack(UMSG_ACK, &m_iAckSeqNo, data, ACKD_FIELD_SIZE * ACKD_TOTAL_SIZE_SMALL);
- }
- ctrlpkt.m_iID = m_PeerID;
- setPacketTS(ctrlpkt, steady_clock::now());
- nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr);
- DebugAck(CONID() + "sendCtrl(UMSG_ACK): ", local_prevack, ack);
- m_ACKWindow.store(m_iAckSeqNo, m_iRcvLastAck);
- enterCS(m_StatsLock);
- m_stats.rcvr.sentAck.count(1);
- leaveCS(m_StatsLock);
- }
- else
- {
- HLOGC(xtlog.Debug, log << CONID() << "sendCtrl(UMSG_ACK): " << "ACK %" << m_iRcvLastAck
- << " <=% ACKACK %" << m_iRcvLastAckAck << " - NOT SENDING ACK");
- }
- return nbsent;
- }
- void srt::CUDT::updateSndLossListOnACK(int32_t ackdata_seqno)
- {
- #if ENABLE_BONDING
- // This is for the call of CSndBuffer::getMsgNoAt that returns
- // this value as a notfound-trap.
- int32_t msgno_at_last_acked_seq = SRT_MSGNO_CONTROL;
- bool is_group = m_parent->m_GroupOf;
- #endif
- // Update sender's loss list and acknowledge packets in the sender's buffer
- {
- // m_RecvAckLock protects sender's loss list and epoll
- ScopedLock ack_lock(m_RecvAckLock);
- const int offset = CSeqNo::seqoff(m_iSndLastDataAck, ackdata_seqno);
- // IF distance between m_iSndLastDataAck and ack is nonempty...
- if (offset <= 0)
- return;
- // update sending variables
- m_iSndLastDataAck = ackdata_seqno;
- #if ENABLE_BONDING
- if (is_group)
- {
- // Get offset-1 because 'offset' points actually to past-the-end
- // of the sender buffer. We have already checked that offset is
- // at least 1.
- msgno_at_last_acked_seq = m_pSndBuffer->getMsgNoAt(offset-1);
- // Just keep this value prepared; it can't be updated exactly right
- // now because accessing the group needs some locks to be applied
- // with preserved the right locking order.
- }
- #endif
- // remove any loss that predates 'ack' (not to be considered loss anymore)
- m_pSndLossList->removeUpTo(CSeqNo::decseq(m_iSndLastDataAck));
- // acknowledge the sending buffer (remove data that predate 'ack')
- m_pSndBuffer->ackData(offset);
- // acknowledde any waiting epolls to write
- uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, true);
- CGlobEvent::triggerEvent();
- }
- #if ENABLE_BONDING
- if (is_group)
- {
- // m_RecvAckLock is ordered AFTER m_GlobControlLock, so this can only
- // be done now that m_RecvAckLock is unlocked.
- ScopedLock glock (uglobal().m_GlobControlLock);
- if (m_parent->m_GroupOf)
- {
- HLOGC(inlog.Debug, log << CONID() << "ACK: acking group sender buffer for #" << msgno_at_last_acked_seq);
- // Guard access to m_iSndAckedMsgNo field
- // Note: This can't be done inside CUDTGroup::ackMessage
- // because this function is also called from CUDT::sndDropTooLate
- // called from CUDT::sendmsg2 called from CUDTGroup::send, which
- // applies the lock on m_GroupLock already.
- ScopedLock glk (*m_parent->m_GroupOf->exp_groupLock());
- // NOTE: ackMessage also accepts and ignores the trap representation
- // which is SRT_MSGNO_CONTROL.
- m_parent->m_GroupOf->ackMessage(msgno_at_last_acked_seq);
- }
- }
- #endif
- // insert this socket to snd list if it is not on the list yet
- const steady_clock::time_point currtime = steady_clock::now();
- m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE, currtime);
- if (m_config.bSynSending)
- {
- CSync::lock_notify_one(m_SendBlockCond, m_SendBlockLock);
- }
- // record total time used for sending
- enterCS(m_StatsLock);
- m_stats.sndDuration += count_microseconds(currtime - m_stats.sndDurationCounter);
- m_stats.m_sndDurationTotal += count_microseconds(currtime - m_stats.sndDurationCounter);
- m_stats.sndDurationCounter = currtime;
- leaveCS(m_StatsLock);
- }
- void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_point& currtime)
- {
- const int32_t* ackdata = (const int32_t*)ctrlpkt.m_pcData;
- const int32_t ackdata_seqno = ackdata[ACKD_RCVLASTACK];
- // Check the value of ACK in case when it was some rogue peer
- if (ackdata_seqno < 0)
- {
- // This embraces all cases when the most significant bit is set,
- // as the variable is of a signed type. So, SRT_SEQNO_NONE is
- // included, but it also triggers for any other kind of invalid value.
- // This check MUST BE DONE before making any operation on this number.
- LOGC(inlog.Error, log << CONID() << "ACK: IPE/EPE: received invalid ACK value: " << ackdata_seqno
- << " " << std::hex << ackdata_seqno << " (IGNORED)");
- return;
- }
- const bool isLiteAck = ctrlpkt.getLength() == (size_t)SEND_LITE_ACK;
- HLOGC(inlog.Debug,
- log << CONID() << "ACK covers: " << m_iSndLastDataAck << " - " << ackdata_seqno << " [ACK=" << m_iSndLastAck
- << "]" << (isLiteAck ? "[LITE]" : "[FULL]"));
- updateSndLossListOnACK(ackdata_seqno);
- // Process a lite ACK
- if (isLiteAck)
- {
- if (CSeqNo::seqcmp(ackdata_seqno, m_iSndLastAck) >= 0)
- {
- ScopedLock ack_lock(m_RecvAckLock);
- m_iFlowWindowSize = m_iFlowWindowSize - CSeqNo::seqoff(m_iSndLastAck, ackdata_seqno);
- m_iSndLastAck = ackdata_seqno;
- m_tsLastRspAckTime = currtime;
- m_iReXmitCount = 1; // Reset re-transmit count since last ACK
- }
- return;
- }
- // Decide to send ACKACK or not
- {
- // Sequence number of the ACK packet
- const int32_t ack_seqno = ctrlpkt.getAckSeqNo();
- // Send ACK acknowledgement (UMSG_ACKACK).
- // There can be less ACKACK packets in the stream, than the number of ACK packets.
- // Only send ACKACK every syn interval or if ACK packet with the sequence number
- // already acknowledged (with ACKACK) has come again, which probably means ACKACK was lost.
- if ((currtime - m_SndLastAck2Time > microseconds_from(COMM_SYN_INTERVAL_US)) || (ack_seqno == m_iSndLastAck2))
- {
- sendCtrl(UMSG_ACKACK, &ack_seqno);
- m_iSndLastAck2 = ack_seqno;
- m_SndLastAck2Time = currtime;
- }
- }
- //
- // Begin of the new code with TLPKTDROP.
- //
- // Protect packet retransmission
- {
- ScopedLock ack_lock(m_RecvAckLock);
- // Check the validation of the ack
- if (CSeqNo::seqcmp(ackdata_seqno, CSeqNo::incseq(m_iSndCurrSeqNo)) > 0)
- {
- // this should not happen: attack or bug
- LOGC(gglog.Error,
- log << CONID() << "ATTACK/IPE: incoming ack seq " << ackdata_seqno << " exceeds current "
- << m_iSndCurrSeqNo << " by " << (CSeqNo::seqoff(m_iSndCurrSeqNo, ackdata_seqno) - 1) << "!");
- m_bBroken = true;
- m_iBrokenCounter = 0;
- return;
- }
- if (CSeqNo::seqcmp(ackdata_seqno, m_iSndLastAck) >= 0)
- {
- const int cwnd1 = std::min(int(m_iFlowWindowSize), int(m_dCongestionWindow));
- const bool bWasStuck = cwnd1<= getFlightSpan();
- // Update Flow Window Size, must update before and together with m_iSndLastAck
- m_iFlowWindowSize = ackdata[ACKD_BUFFERLEFT];
- m_iSndLastAck = ackdata_seqno;
- m_tsLastRspAckTime = currtime;
- m_iReXmitCount = 1; // Reset re-transmit count since last ACK
- const int cwnd = std::min(int(m_iFlowWindowSize), int(m_dCongestionWindow));
- if (bWasStuck && cwnd > getFlightSpan())
- {
- m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE);
- HLOGC(gglog.Debug,
- log << CONID() << "processCtrlAck: could reschedule SND. iFlowWindowSize " << m_iFlowWindowSize
- << " SPAN " << getFlightSpan() << " ackdataseqno %" << ackdata_seqno);
- }
- }
- /*
- * We must not ignore full ack received by peer
- * if data has been artificially acked by late packet drop.
- * Therefore, a distinct ack state is used for received Ack (iSndLastFullAck)
- * and ack position in send buffer (m_iSndLastDataAck).
- * Otherwise, when severe congestion causing packet drops (and m_iSndLastDataAck update)
- * occures, we drop received acks (as duplicates) and do not update stats like RTT,
- * which may go crazy and stay there, preventing proper stream recovery.
- */
- if (CSeqNo::seqoff(m_iSndLastFullAck, ackdata_seqno) <= 0)
- {
- // discard it if it is a repeated ACK
- return;
- }
- m_iSndLastFullAck = ackdata_seqno;
- }
- //
- // END of the new code with TLPKTDROP
- //
- #if ENABLE_BONDING
- if (m_parent->m_GroupOf)
- {
- ScopedLock glock (uglobal().m_GlobControlLock);
- if (m_parent->m_GroupOf)
- {
- // Will apply m_GroupLock, ordered after m_GlobControlLock.
- // m_GlobControlLock is necessary for group existence.
- m_parent->m_GroupOf->updateWriteState();
- }
- }
- #endif
- size_t acksize = ctrlpkt.getLength(); // TEMPORARY VALUE FOR CHECKING
- bool wrongsize = 0 != (acksize % ACKD_FIELD_SIZE);
- acksize = acksize / ACKD_FIELD_SIZE; // ACTUAL VALUE
- if (wrongsize)
- {
- // Issue a log, but don't do anything but skipping the "odd" bytes from the payload.
- LOGC(inlog.Warn,
- log << CONID() << "Received UMSG_ACK payload is not evened up to 4-byte based field size - cutting to "
- << acksize << " fields");
- }
- // Start with checking the base size.
- if (acksize < ACKD_TOTAL_SIZE_SMALL)
- {
- LOGC(inlog.Warn, log << CONID() << "Invalid ACK size " << acksize << " fields - less than minimum required!");
- // Ack is already interpreted, just skip further parts.
- return;
- }
- // This check covers fields up to ACKD_BUFFERLEFT.
- // Extract RTT estimate and RTTVar from the ACK packet.
- const int rtt = ackdata[ACKD_RTT];
- const int rttvar = ackdata[ACKD_RTTVAR];
- // Update the values of smoothed RTT and the variation in RTT samples
- // on subsequent RTT estimates extracted from the ACK packets
- // (during transmission).
- if (m_bIsFirstRTTReceived)
- {
- // Suppose transmission is bidirectional if sender is also receiving
- // data packets.
- enterCS(m_StatsLock);
- const bool bPktsReceived = m_stats.rcvr.recvd.total.count() != 0;
- leaveCS(m_StatsLock);
- if (bPktsReceived) // Transmission is bidirectional.
- {
- // RTT value extracted from the ACK packet (rtt) is already smoothed
- // RTT obtained at the receiver side. Apply EWMA anyway for the second
- // time on the sender side. Ignore initial values which might arrive
- // after the smoothed RTT on the sender side has been
- // reset to the very first RTT sample received from the receiver.
- // TODO: The case of bidirectional transmission requires further
- // improvements and testing. Double smoothing is applied here to be
- // consistent with the previous behavior.
- if (rtt != INITIAL_RTT || rttvar != INITIAL_RTTVAR)
- {
- int iSRTT = m_iSRTT.load(), iRTTVar = m_iRTTVar.load();
- iRTTVar = avg_iir<4>(iRTTVar, abs(rtt - iSRTT));
- iSRTT = avg_iir<8>(iSRTT, rtt);
- m_iSRTT = iSRTT;
- m_iRTTVar = iRTTVar;
- }
- }
- else // Transmission is unidirectional.
- {
- // Simply take the values of smoothed RTT and RTT variance from
- // the ACK packet.
- m_iSRTT = rtt;
- m_iRTTVar = rttvar;
- }
- }
- // Reset the value of smoothed RTT to the first real RTT estimate extracted
- // from an ACK after initialization (at the beginning of transmission).
- // In case of resumed connection over the same network, the very first RTT
- // value sent within an ACK will be taken from cache and equal to previous
- // connection's final smoothed RTT value. The reception of such a value
- // will also trigger the smoothed RTT reset at the sender side.
- else if (rtt != INITIAL_RTT && rttvar != INITIAL_RTTVAR)
- {
- m_iSRTT = rtt;
- m_iRTTVar = rttvar;
- m_bIsFirstRTTReceived = true;
- }
- #if SRT_DEBUG_RTT
- s_rtt_trace.trace(currtime, "ACK", rtt, rttvar, m_bIsFirstRTTReceived,
- m_stats.recvTotal, m_iSRTT, m_iRTTVar);
- #endif
- /* Version-dependent fields:
- * Original UDT (total size: ACKD_TOTAL_SIZE_SMALL):
- * ACKD_RCVLASTACK
- * ACKD_RTT
- * ACKD_RTTVAR
- * ACKD_BUFFERLEFT
- * Additional UDT fields, not always attached:
- * ACKD_RCVSPEED
- * ACKD_BANDWIDTH
- * SRT extension since v1.0.1:
- * ACKD_RCVRATE
- * SRT extension in v1.0.2 only:
- * ACKD_XMRATE_VER102_ONLY
- */
- if (acksize > ACKD_TOTAL_SIZE_SMALL)
- {
- // This means that ACKD_RCVSPEED and ACKD_BANDWIDTH fields are available.
- int pktps = ackdata[ACKD_RCVSPEED];
- int bandwidth = ackdata[ACKD_BANDWIDTH];
- int bytesps;
- /* SRT v1.0.2 Bytes-based stats: bandwidth (pcData[ACKD_XMRATE_VER102_ONLY]) and delivery rate (pcData[ACKD_RCVRATE]) in
- * bytes/sec instead of pkts/sec */
- /* SRT v1.0.3 Bytes-based stats: only delivery rate (pcData[ACKD_RCVRATE]) in bytes/sec instead of pkts/sec */
- if (acksize > ACKD_TOTAL_SIZE_UDTBASE)
- bytesps = ackdata[ACKD_RCVRATE];
- else
- bytesps = pktps * m_iMaxSRTPayloadSize;
- m_iBandwidth = avg_iir<8>(m_iBandwidth.load(), bandwidth);
- m_iDeliveryRate = avg_iir<8>(m_iDeliveryRate.load(), pktps);
- m_iByteDeliveryRate = avg_iir<8>(m_iByteDeliveryRate.load(), bytesps);
- // Update Estimated Bandwidth and packet delivery rate
- // m_iRcvRate = m_iDeliveryRate;
- // ^^ This has been removed because with the SrtCongestion class
- // instead of reading the m_iRcvRate local field this will read
- // cudt->deliveryRate() instead.
- }
- updateCC(TEV_ACK, EventVariant(ackdata_seqno));
- enterCS(m_StatsLock);
- m_stats.sndr.recvdAck.count(1);
- leaveCS(m_StatsLock);
- }
- void srt::CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival)
- {
- int32_t ack = 0;
- // Calculate RTT estimate on the receiver side based on ACK/ACKACK pair.
- const int rtt = m_ACKWindow.acknowledge(ctrlpkt.getAckSeqNo(), ack, tsArrival);
- if (rtt == -1)
- {
- if (ctrlpkt.getAckSeqNo() > (m_iAckSeqNo - static_cast<int>(ACK_WND_SIZE)) && ctrlpkt.getAckSeqNo() <= m_iAckSeqNo)
- {
- LOGC(inlog.Note,
- log << CONID() << "ACKACK out of order, skipping RTT calculation "
- << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo
- << ", RTT (EWMA): " << m_iSRTT << ")");
- return;
- }
- LOGC(inlog.Error,
- log << CONID() << "ACK record not found, can't estimate RTT "
- << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo
- << ", RTT (EWMA): " << m_iSRTT << ")");
- return;
- }
- if (rtt <= 0)
- {
- LOGC(inlog.Error,
- log << CONID() << "IPE: invalid RTT estimate " << rtt
- << ", possible time shift. Clock: " << SRT_SYNC_CLOCK_STR);
- return;
- }
- // If increasing delay is detected.
- // sendCtrl(UMSG_CGWARNING);
- // Update the values of smoothed RTT and the variation in RTT samples
- // on subsequent RTT samples (during transmission).
- if (m_bIsFirstRTTReceived)
- {
- m_iRTTVar = avg_iir<4>(m_iRTTVar.load(), abs(rtt - m_iSRTT.load()));
- m_iSRTT = avg_iir<8>(m_iSRTT.load(), rtt);
- }
- // Reset the value of smoothed RTT on the first RTT sample after initialization
- // (at the beginning of transmission).
- // In case of resumed connection over the same network, the initial RTT
- // value will be taken from cache and equal to previous connection's
- // final smoothed RTT value.
- else
- {
- m_iSRTT = rtt;
- m_iRTTVar = rtt / 2;
- m_bIsFirstRTTReceived = true;
- }
- #if SRT_DEBUG_RTT
- s_rtt_trace.trace(tsArrival, "ACKACK", rtt, -1, m_bIsFirstRTTReceived,
- -1, m_iSRTT, m_iRTTVar);
- #endif
- updateCC(TEV_ACKACK, EventVariant(ack));
- // This function will put a lock on m_RecvLock by itself, as needed.
- // It must be done inside because this function reads the current time
- // and if waiting for the lock has caused a delay, the time will be
- // inaccurate. Additionally it won't lock if TSBPD mode is off, and
- // won't update anything. Note that if you set TSBPD mode and use
- // srt_recvfile (which doesn't make any sense), you'll have a deadlock.
- if (m_config.bDriftTracer)
- {
- const bool drift_updated SRT_ATR_UNUSED = m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), tsArrival, rtt);
- #if ENABLE_BONDING
- if (drift_updated && m_parent->m_GroupOf)
- {
- ScopedLock glock(uglobal().m_GlobControlLock);
- if (m_parent->m_GroupOf)
- {
- m_parent->m_GroupOf->synchronizeDrift(this);
- }
- }
- #endif
- }
- // Update last ACK that has been received by the sender
- if (CSeqNo::seqcmp(ack, m_iRcvLastAckAck) > 0)
- m_iRcvLastAckAck = ack;
- }
- void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt)
- {
- const int32_t* losslist = (int32_t*)(ctrlpkt.m_pcData);
- const size_t losslist_len = ctrlpkt.getLength() / 4;
- bool secure = true;
- // This variable is used in "normal" logs, so it may cause a warning
- // when logging is forcefully off.
- int32_t wrong_loss SRT_ATR_UNUSED = SRT_SEQNO_NONE;
- // protect packet retransmission
- {
- ScopedLock ack_lock(m_RecvAckLock);
- // decode loss list message and insert loss into the sender loss list
- for (int i = 0, n = (int)losslist_len; i < n; ++i)
- {
- // IF the loss is a range <LO, HI>
- if (IsSet(losslist[i], LOSSDATA_SEQNO_RANGE_FIRST))
- {
- // Then it's this is a <LO, HI> specification with HI in a consecutive cell.
- const int32_t losslist_lo = SEQNO_VALUE::unwrap(losslist[i]);
- const int32_t losslist_hi = losslist[i + 1];
- // <LO, HI> specification means that the consecutive cell has been already interpreted.
- ++i;
- HLOGC(inlog.Debug, log << CONID() << "received UMSG_LOSSREPORT: "
- << losslist_lo << "-" << losslist_hi
- << " (" << CSeqNo::seqlen(losslist_lo, losslist_hi) << " packets)...");
- if ((CSeqNo::seqcmp(losslist_lo, losslist_hi) > 0) ||
- (CSeqNo::seqcmp(losslist_hi, m_iSndCurrSeqNo) > 0))
- {
- // LO must not be greater than HI.
- // HI must not be greater than the most recent sent seq.
- LOGC(inlog.Warn, log << CONID() << "rcv LOSSREPORT rng " << losslist_lo << " - " << losslist_hi
- << " with last sent " << m_iSndCurrSeqNo << " - DISCARDING");
- secure = false;
- wrong_loss = losslist_hi;
- break;
- }
- int num = 0;
- // IF losslist_lo %>= m_iSndLastAck
- if (CSeqNo::seqcmp(losslist_lo, m_iSndLastAck) >= 0)
- {
- HLOGC(inlog.Debug, log << CONID() << "LOSSREPORT: adding "
- << losslist_lo << " - " << losslist_hi << " to loss list");
- num = m_pSndLossList->insert(losslist_lo, losslist_hi);
- }
- // ELSE losslist_lo %< m_iSndLastAck
- else
- {
- // This should be theoretically impossible because this would mean that
- // the received packet loss report informs about the loss that predates
- // the ACK sequence.
- // However, this can happen in these situations:
- // - if the packet reordering has caused the earlier sent LOSSREPORT will be
- // delivered after later sent ACK. Whatever, ACK should be more important,
- // so simply drop the part that predates ACK.
- // - redundancy second link (ISN was screwed up initially, but late towards last sent)
- // - initial DROPREQ was lost
- // This just causes repeating DROPREQ, as when the receiver continues sending
- // LOSSREPORT, it's probably UNAWARE OF THE SITUATION.
- int32_t dropreq_hi = losslist_hi;
- IF_HEAVY_LOGGING(const char* drop_type = "completely");
- // IF losslist_hi %>= m_iSndLastAck
- if (CSeqNo::seqcmp(losslist_hi, m_iSndLastAck) >= 0)
- {
- HLOGC(inlog.Debug, log << CONID() << "LOSSREPORT: adding "
- << m_iSndLastAck << "[ACK] - " << losslist_hi << " to loss list");
- num = m_pSndLossList->insert(m_iSndLastAck, losslist_hi);
- dropreq_hi = CSeqNo::decseq(m_iSndLastAck);
- IF_HEAVY_LOGGING(drop_type = "partially");
- }
- // In distinction to losslist, DROPREQ has always just one range,
- // and the data are <LO, HI>, with no range bit.
- int32_t seqpair[2] = { losslist_lo, dropreq_hi };
- const int32_t no_msgno = 0; // We don't know.
- // When this DROPREQ gets lost in UDP again, the receiver will do one of these:
- // - repeatedly send LOSSREPORT (as per NAKREPORT), so this will happen again
- // - finally give up rexmit request as per TLPKTDROP (DROPREQ should make
- // TSBPD wake up should it still wait for new packets to get ACK-ed)
- HLOGC(inlog.Debug,
- log << CONID() << "LOSSREPORT: " << drop_type << " IGNORED with SndLastAck=%" << m_iSndLastAck
- << ": %" << losslist_lo << "-" << dropreq_hi << " - sending DROPREQ");
- sendCtrl(UMSG_DROPREQ, &no_msgno, seqpair, sizeof(seqpair));
- }
- enterCS(m_StatsLock);
- m_stats.sndr.lost.count(num);
- leaveCS(m_StatsLock);
- }
- // ELSE the loss is a single seq
- else
- {
- // IF loss_seq %>= m_iSndLastAck
- if (CSeqNo::seqcmp(losslist[i], m_iSndLastAck) >= 0)
- {
- if (CSeqNo::seqcmp(losslist[i], m_iSndCurrSeqNo) > 0)
- {
- LOGC(inlog.Warn, log << CONID() << "rcv LOSSREPORT pkt %" << losslist[i]
- << " with last sent %" << m_iSndCurrSeqNo << " - DISCARDING");
- // loss_seq must not be greater than the most recent sent seq
- secure = false;
- wrong_loss = losslist[i];
- break;
- }
- HLOGC(inlog.Debug,
- log << CONID() << "LOSSREPORT: adding %" << losslist[i] << " (1 packet) to loss list");
- const int num = m_pSndLossList->insert(losslist[i], losslist[i]);
- enterCS(m_StatsLock);
- m_stats.sndr.lost.count(num);
- leaveCS(m_StatsLock);
- }
- // ELSE loss_seq %< m_iSndLastAck
- else
- {
- // In distinction to losslist, DROPREQ has always just one range,
- // and the data are <LO, HI>, with no range bit.
- int32_t seqpair[2] = { losslist[i], losslist[i] };
- const int32_t no_msgno = 0; // We don't know.
- HLOGC(inlog.Debug,
- log << CONID() << "LOSSREPORT: IGNORED with SndLastAck=%" << m_iSndLastAck << ": %" << losslist[i]
- << " - sending DROPREQ");
- sendCtrl(UMSG_DROPREQ, &no_msgno, seqpair, sizeof(seqpair));
- }
- }
- }
- }
- updateCC(TEV_LOSSREPORT, EventVariant(losslist, losslist_len));
- if (!secure)
- {
- LOGC(inlog.Warn,
- log << CONID() << "out-of-band LOSSREPORT received; BUG or ATTACK - last sent %" << m_iSndCurrSeqNo
- << " vs loss %" << wrong_loss);
- // this should not happen: attack or bug
- m_bBroken = true;
- m_iBrokenCounter = 0;
- return;
- }
- // the lost packet (retransmission) should be sent out immediately
- m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE);
- enterCS(m_StatsLock);
- m_stats.sndr.recvdNak.count(1);
- leaveCS(m_StatsLock);
- }
- void srt::CUDT::processCtrlHS(const CPacket& ctrlpkt)
- {
- CHandShake req;
- req.load_from(ctrlpkt.m_pcData, ctrlpkt.getLength());
- HLOGC(inlog.Debug, log << CONID() << "processCtrl: got HS: " << req.show());
- if ((req.m_iReqType > URQ_INDUCTION_TYPES) // acually it catches URQ_INDUCTION and URQ_ERROR_* symbols...???
- || (m_config.bRendezvous && (req.m_iReqType != URQ_AGREEMENT))) // rnd sends AGREEMENT in rsp to CONCLUSION
- {
- // The peer side has not received the handshake message, so it keeps querying
- // resend the handshake packet
- // This condition embraces cases when:
- // - this is normal accept() and URQ_INDUCTION was received
- // - this is rendezvous accept() and there's coming any kind of URQ except AGREEMENT (should be RENDEZVOUS
- // or CONCLUSION)
- // - this is any of URQ_ERROR_* - well...
- CHandShake initdata;
- initdata.m_iISN = m_iISN;
- initdata.m_iMSS = m_config.iMSS;
- initdata.m_iFlightFlagSize = m_config.iFlightFlagSize;
- // For rendezvous we do URQ_WAVEAHAND/URQ_CONCLUSION --> URQ_AGREEMENT.
- // For client-server we do URQ_INDUCTION --> URQ_CONCLUSION.
- initdata.m_iReqType = (!m_config.bRendezvous) ? URQ_CONCLUSION : URQ_AGREEMENT;
- initdata.m_iID = m_SocketID;
- uint32_t kmdata[SRTDATA_MAXSIZE];
- size_t kmdatasize = SRTDATA_MAXSIZE;
- bool have_hsreq = false;
- if (req.m_iVersion > HS_VERSION_UDT4)
- {
- initdata.m_iVersion = HS_VERSION_SRT1; // if I remember correctly, this is induction/listener...
- const int hs_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType);
- if (hs_flags != 0) // has SRT extensions
- {
- HLOGC(inlog.Debug,
- log << CONID() << "processCtrl/HS: got HS reqtype=" << RequestTypeStr(req.m_iReqType)
- << " WITH SRT ext");
- have_hsreq = interpretSrtHandshake(req, ctrlpkt, (kmdata), (&kmdatasize));
- if (!have_hsreq)
- {
- initdata.m_iVersion = 0;
- m_RejectReason = SRT_REJ_ROGUE;
- initdata.m_iReqType = URQFailure(m_RejectReason);
- }
- else
- {
- // Extensions are added only in case of CONCLUSION (not AGREEMENT).
- // Actually what is expected here is that this may either process the
- // belated-repeated handshake from a caller (and then it's CONCLUSION,
- // and should be added with HSRSP/KMRSP), or it's a belated handshake
- // of Rendezvous when it has already considered itself connected.
- // Sanity check - according to the rules, there should be no such situation
- if (m_config.bRendezvous && m_SrtHsSide == HSD_RESPONDER)
- {
- LOGC(inlog.Error,
- log << CONID() << "processCtrl/HS: IPE???: RESPONDER should receive all its handshakes in "
- "handshake phase.");
- }
- // The 'extension' flag will be set from this variable; set it to false
- // in case when the AGREEMENT response is to be sent.
- have_hsreq = initdata.m_iReqType == URQ_CONCLUSION;
- HLOGC(inlog.Debug,
- log << CONID() << "processCtrl/HS: processing ok, reqtype=" << RequestTypeStr(initdata.m_iReqType)
- << " kmdatasize=" << kmdatasize);
- }
- }
- else
- {
- HLOGC(inlog.Debug, log << CONID() << "processCtrl/HS: got HS reqtype=" << RequestTypeStr(req.m_iReqType));
- }
- }
- else
- {
- initdata.m_iVersion = HS_VERSION_UDT4;
- kmdatasize = 0; // HSv4 doesn't add any extensions, no KMX
- }
- initdata.m_extension = have_hsreq;
- HLOGC(inlog.Debug,
- log << CONID() << "processCtrl: responding HS reqtype=" << RequestTypeStr(initdata.m_iReqType)
- << (have_hsreq ? " WITH SRT HS response extensions" : ""));
- CPacket response;
- response.setControl(UMSG_HANDSHAKE);
- response.allocate(m_iMaxSRTPayloadSize);
- // If createSrtHandshake failed, don't send anything. Actually it can only fail on IPE.
- // There is also no possible IPE condition in case of HSv4 - for this version it will always return true.
- if (createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize,
- (response), (initdata)))
- {
- response.m_iID = m_PeerID;
- setPacketTS(response, steady_clock::now());
- const int nbsent = m_pSndQueue->sendto(m_PeerAddr, response, m_SourceAddr);
- if (nbsent)
- {
- m_tsLastSndTime.store(steady_clock::now());
- }
- }
- }
- else
- {
- HLOGC(inlog.Debug, log << CONID() << "processCtrl: ... not INDUCTION, not ERROR, not rendezvous - IGNORED.");
- }
- }
- void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt)
- {
- const int32_t* dropdata = (const int32_t*) ctrlpkt.m_pcData;
- {
- CUniqueSync rcvtscc (m_RecvLock, m_RcvTsbPdCond);
- // With both TLPktDrop and TsbPd enabled, a message always consists only of one packet.
- // It will be dropped as too late anyway. Not dropping it from the receiver buffer
- // in advance reduces false drops if the packet somehow manages to arrive.
- // Still remove the record from the loss list to cease further retransmission requests.
- if (!m_bTLPktDrop || !m_bTsbPd)
- {
- const bool using_rexmit_flag = m_bPeerRexmitFlag;
- ScopedLock rblock(m_RcvBufferLock);
- const int iDropCnt = m_pRcvBuffer->dropMessage(dropdata[0], dropdata[1], ctrlpkt.getMsgSeq(using_rexmit_flag), CRcvBuffer::KEEP_EXISTING);
- if (iDropCnt > 0)
- {
- LOGC(brlog.Warn, log << CONID() << "RCV-DROPPED " << iDropCnt << " packet(s), seqno range %"
- << dropdata[0] << "-%" << dropdata[1] << ", msgno " << ctrlpkt.getMsgSeq(using_rexmit_flag)
- << " (SND DROP REQUEST).");
- enterCS(m_StatsLock);
- // Estimate dropped bytes from average payload size.
- const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize();
- m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt));
- leaveCS(m_StatsLock);
- }
- }
- // When the drop request was received, it means that there are
- // packets for which there will never be ACK sent; if the TSBPD thread
- // is currently in the ACK-waiting state, it may never exit.
- if (m_bTsbPd)
- {
- HLOGP(inlog.Debug, "DROPREQ: signal TSBPD");
- rcvtscc.notify_one();
- }
- }
- dropFromLossLists(dropdata[0], dropdata[1]);
- // If dropping ahead of the current largest sequence number,
- // move the recv seq number forward.
- if ((CSeqNo::seqcmp(dropdata[0], CSeqNo::incseq(m_iRcvCurrSeqNo)) <= 0)
- && (CSeqNo::seqcmp(dropdata[1], m_iRcvCurrSeqNo) > 0))
- {
- HLOGC(inlog.Debug, log << CONID() << "DROPREQ: dropping %"
- << dropdata[0] << "-" << dropdata[1] << " <-- set as current seq");
- m_iRcvCurrSeqNo = dropdata[1];
- }
- else
- {
- HLOGC(inlog.Debug, log << CONID() << "DROPREQ: dropping %"
- << dropdata[0] << "-" << dropdata[1] << " current %" << m_iRcvCurrSeqNo);
- }
- }
- void srt::CUDT::processCtrlShutdown()
- {
- m_bShutdown = true;
- m_bClosing = true;
- m_bBroken = true;
- m_iBrokenCounter = 60;
- // This does the same as it would happen on connection timeout,
- // just we know about this state prematurely thanks to this message.
- updateBrokenConnection();
- completeBrokenConnectionDependencies(SRT_ECONNLOST); // LOCKS!
- }
- void srt::CUDT::processCtrlUserDefined(const CPacket& ctrlpkt)
- {
- HLOGC(inlog.Debug, log << CONID() << "CONTROL EXT MSG RECEIVED:"
- << MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType())
- << ", value=" << ctrlpkt.getExtendedType());
- // This has currently two roles in SRT:
- // - HSv4 (legacy) handshake
- // - refreshed KMX (initial KMX is done still in the HS process in HSv5)
- const bool understood = processSrtMsg(&ctrlpkt);
- // CAREFUL HERE! This only means that this update comes from the UMSG_EXT
- // message received, REGARDLESS OF WHAT IT IS. This version doesn't mean
- // the handshake version, but the reason of calling this function.
- //
- // Fortunately, the only messages taken into account in this function
- // are HSREQ and HSRSP, which should *never* be interchanged when both
- // parties are HSv5.
- if (understood)
- {
- if (ctrlpkt.getExtendedType() == SRT_CMD_HSREQ || ctrlpkt.getExtendedType() == SRT_CMD_HSRSP)
- {
- updateAfterSrtHandshake(HS_VERSION_UDT4);
- }
- }
- else
- {
- updateCC(TEV_CUSTOM, EventVariant(&ctrlpkt));
- }
- }
- void srt::CUDT::processCtrl(const CPacket &ctrlpkt)
- {
- // Just heard from the peer, reset the expiration count.
- m_iEXPCount = 1;
- const steady_clock::time_point currtime = steady_clock::now();
- m_tsLastRspTime = currtime;
- HLOGC(inlog.Debug,
- log << CONID() << "incoming UMSG:" << ctrlpkt.getType() << " ("
- << MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType()) << ") socket=%" << ctrlpkt.m_iID);
- switch (ctrlpkt.getType())
- {
- case UMSG_ACK: // 010 - Acknowledgement
- processCtrlAck(ctrlpkt, currtime);
- break;
- case UMSG_ACKACK: // 110 - Acknowledgement of Acknowledgement
- processCtrlAckAck(ctrlpkt, currtime);
- break;
- case UMSG_LOSSREPORT: // 011 - Loss Report
- processCtrlLossReport(ctrlpkt);
- break;
- case UMSG_CGWARNING: // 100 - Delay Warning
- // One way packet delay is increasing, so decrease the sending rate
- m_tdSendInterval = (m_tdSendInterval.load() * 1125) / 1000;
- // XXX Note as interesting fact: this is only prepared for handling,
- // but nothing in the code is sending this message. Probably predicted
- // for a custom congctl. There's a predicted place to call it under
- // UMSG_ACKACK handling, but it's commented out.
- break;
- case UMSG_KEEPALIVE: // 001 - Keep-alive
- processKeepalive(ctrlpkt, currtime);
- break;
- case UMSG_HANDSHAKE: // 000 - Handshake
- processCtrlHS(ctrlpkt);
- break;
- case UMSG_SHUTDOWN: // 101 - Shutdown
- processCtrlShutdown();
- break;
- case UMSG_DROPREQ: // 111 - Msg drop request
- processCtrlDropReq(ctrlpkt);
- break;
- case UMSG_PEERERROR: // 1000 - An error has happened to the peer side
- // int err_type = packet.getAddInfo();
- // currently only this error is signalled from the peer side
- // if recvfile() failes (e.g., due to disk fail), blcoked sendfile/send should return immediately
- // giving the app a chance to fix the issue
- m_bPeerHealth = false;
- break;
- case UMSG_EXT: // 0x7FFF - reserved and user defined messages
- processCtrlUserDefined(ctrlpkt);
- break;
- default:
- break;
- }
- }
- void srt::CUDT::updateSrtRcvSettings()
- {
- // CHANGED: we need to apply the tsbpd delay only for socket TSBPD.
- // For Group TSBPD the buffer will have to deliver packets always on request
- // by sequence number, although the buffer will have to solve all the TSBPD
- // things internally anyway. Extracting by sequence number means only that
- // the packet can be retrieved from the buffer before its time to play comes
- // (unlike in normal situation when reading directly from socket), however
- // its time to play shall be properly defined.
- ScopedLock lock(m_RecvLock);
- // NOTE: remember to also update synchronizeWithGroup() if more settings are updated here.
- m_pRcvBuffer->setPeerRexmitFlag(m_bPeerRexmitFlag);
- // XXX m_bGroupTsbPd is ignored with SRT_ENABLE_APP_READER
- if (m_bTsbPd || m_bGroupTsbPd)
- {
- m_pRcvBuffer->setTsbPdMode(m_tsRcvPeerStartTime, false, milliseconds_from(m_iTsbPdDelay_ms));
- HLOGC(cnlog.Debug, log << "AFTER HS: Set Rcv TsbPd mode"
- << (m_bGroupTsbPd ? " (AS GROUP MEMBER)" : "")
- << ": delay=" << (m_iTsbPdDelay_ms / 1000) << "." << (m_iTsbPdDelay_ms % 1000)
- << "s RCV START: " << FormatTime(m_tsRcvPeerStartTime).c_str());
- }
- else
- {
- HLOGC(cnlog.Debug, log << CONID() << "AFTER HS: Rcv TsbPd mode not set");
- }
- }
- void srt::CUDT::updateSrtSndSettings()
- {
- if (m_bPeerTsbPd)
- {
- /* We are TsbPd sender */
- // XXX Check what happened here.
- // m_iPeerTsbPdDelay_ms = m_CongCtl->getSndPeerTsbPdDelay();// + ((m_iSRTT + (4 * m_iRTTVar)) / 1000);
- /*
- * For sender to apply Too-Late Packet Drop
- * option (m_bTLPktDrop) must be enabled and receiving peer shall support it
- */
- HLOGC(cnlog.Debug, log << "AFTER HS: Set Snd TsbPd mode "
- << (m_bPeerTLPktDrop ? "with" : "without")
- << " TLPktDrop: delay=" << (m_iPeerTsbPdDelay_ms/1000) << "." << (m_iPeerTsbPdDelay_ms%1000)
- << "s START TIME: " << FormatTime(m_stats.tsStartTime).c_str());
- }
- else
- {
- HLOGC(cnlog.Debug, log << CONID() << "AFTER HS: Snd TsbPd mode not set");
- }
- }
- void srt::CUDT::updateAfterSrtHandshake(int hsv)
- {
- HLOGC(cnlog.Debug, log << CONID() << "updateAfterSrtHandshake: HS version " << hsv);
- // This is blocked from being run in the "app reader" version because here
- // every socket does its TsbPd independently, just the sequence screwup is
- // done and the application reader sorts out packets by sequence numbers,
- // but only when they are signed off by TsbPd.
- // The only possibility here is one of these two:
- // - Agent is RESPONDER and it receives HSREQ.
- // - Agent is INITIATOR and it receives HSRSP.
- //
- // In HSv4, INITIATOR is sender and RESPONDER is receiver.
- // In HSv5, both are sender AND receiver.
- //
- // This function will be called only ONCE in this
- // instance, through either HSREQ or HSRSP.
- #if ENABLE_HEAVY_LOGGING
- const char* hs_side[] = { "DRAW", "INITIATOR", "RESPONDER" };
- #if ENABLE_BONDING
- string grpspec;
- if (m_parent->m_GroupOf)
- {
- ScopedLock glock (uglobal().m_GlobControlLock);
- grpspec = m_parent->m_GroupOf
- ? " group=$" + Sprint(m_parent->m_GroupOf->id())
- : string();
- }
- #else
- const char* grpspec = "";
- #endif
- HLOGC(cnlog.Debug,
- log << CONID() << "updateAfterSrtHandshake: version=" << m_ConnRes.m_iVersion
- << " side=" << hs_side[m_SrtHsSide] << grpspec);
- #endif
- if (hsv > HS_VERSION_UDT4)
- {
- updateSrtRcvSettings();
- updateSrtSndSettings();
- }
- else if (m_SrtHsSide == HSD_INITIATOR)
- {
- // HSv4 INITIATOR is sender
- updateSrtSndSettings();
- }
- else
- {
- // HSv4 RESPONDER is receiver
- updateSrtRcvSettings();
- }
- }
- int srt::CUDT::packLostData(CPacket& w_packet)
- {
- // protect m_iSndLastDataAck from updating by ACK processing
- UniqueLock ackguard(m_RecvAckLock);
- const steady_clock::time_point time_now = steady_clock::now();
- const steady_clock::time_point time_nak = time_now - microseconds_from(m_iSRTT - 4 * m_iRTTVar);
- while ((w_packet.m_iSeqNo = m_pSndLossList->popLostSeq()) >= 0)
- {
- // XXX See the note above the m_iSndLastDataAck declaration in core.h
- // This is the place where the important sequence numbers for
- // sender buffer are actually managed by this field here.
- const int offset = CSeqNo::seqoff(m_iSndLastDataAck, w_packet.m_iSeqNo);
- if (offset < 0)
- {
- // XXX Likely that this will never be executed because if the upper
- // sequence is not in the sender buffer, then most likely the loss
- // was completely ignored.
- LOGC(qrlog.Error,
- log << CONID() << "IPE/EPE: packLostData: LOST packet negative offset: seqoff(m_iSeqNo "
- << w_packet.m_iSeqNo << ", m_iSndLastDataAck " << m_iSndLastDataAck << ")=" << offset
- << ". Continue");
- // No matter whether this is right or not (maybe the attack case should be
- // considered, and some LOSSREPORT flood prevention), send the drop request
- // to the peer.
- int32_t seqpair[2] = {
- w_packet.m_iSeqNo,
- CSeqNo::decseq(m_iSndLastDataAck)
- };
- w_packet.m_iMsgNo = 0; // Message number is not known, setting all 32 bits to 0.
- HLOGC(qrlog.Debug,
- log << CONID() << "PEER reported LOSS not from the sending buffer - requesting DROP: msg="
- << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " SEQ:" << seqpair[0] << " - " << seqpair[1] << "("
- << (-offset) << " packets)");
- sendCtrl(UMSG_DROPREQ, &w_packet.m_iMsgNo, seqpair, sizeof(seqpair));
- continue;
- }
- if (m_bPeerNakReport && m_config.iRetransmitAlgo != 0)
- {
- const steady_clock::time_point tsLastRexmit = m_pSndBuffer->getPacketRexmitTime(offset);
- if (tsLastRexmit >= time_nak)
- {
- HLOGC(qrlog.Debug, log << CONID() << "REXMIT: ignoring seqno "
- << w_packet.m_iSeqNo << ", last rexmit " << (is_zero(tsLastRexmit) ? "never" : FormatTime(tsLastRexmit))
- << " RTT=" << m_iSRTT << " RTTVar=" << m_iRTTVar
- << " now=" << FormatTime(time_now));
- continue;
- }
- }
- int msglen;
- steady_clock::time_point tsOrigin;
- const int payload = m_pSndBuffer->readData(offset, (w_packet), (tsOrigin), (msglen));
- if (payload == -1)
- {
- int32_t seqpair[2];
- seqpair[0] = w_packet.m_iSeqNo;
- SRT_ASSERT(msglen >= 1);
- seqpair[1] = CSeqNo::incseq(seqpair[0], msglen - 1);
- HLOGC(qrlog.Debug,
- log << CONID() << "loss-reported packets expired in SndBuf - requesting DROP: msgno="
- << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " msglen=" << msglen << " SEQ:" << seqpair[0] << " - "
- << seqpair[1]);
- sendCtrl(UMSG_DROPREQ, &w_packet.m_iMsgNo, seqpair, sizeof(seqpair));
- // skip all dropped packets
- m_pSndLossList->removeUpTo(seqpair[1]);
- m_iSndCurrSeqNo = CSeqNo::maxseq(m_iSndCurrSeqNo, seqpair[1]);
- continue;
- }
- else if (payload == 0)
- continue;
- // The packet has been ecrypted, thus the authentication tag is expected to be stored
- // in the SND buffer as well right after the payload.
- if (m_pCryptoControl && m_pCryptoControl->getCryptoMode() == CSrtConfig::CIPHER_MODE_AES_GCM)
- {
- w_packet.setLength(w_packet.getLength() + HAICRYPT_AUTHTAG_MAX);
- }
- // At this point we no longer need the ACK lock,
- // because we are going to return from the function.
- // Therefore unlocking in order not to block other threads.
- ackguard.unlock();
- enterCS(m_StatsLock);
- m_stats.sndr.sentRetrans.count(payload);
- leaveCS(m_StatsLock);
- // Despite the contextual interpretation of packet.m_iMsgNo around
- // CSndBuffer::readData version 2 (version 1 doesn't return -1), in this particular
- // case we can be sure that this is exactly the value of PH_MSGNO as a bitset.
- // So, set here the rexmit flag if the peer understands it.
- if (m_bPeerRexmitFlag)
- {
- w_packet.m_iMsgNo |= PACKET_SND_REXMIT;
- }
- setDataPacketTS(w_packet, tsOrigin);
- #ifdef ENABLE_MAXREXMITBW
- m_SndRexmitRate.addSample(time_now, 1, w_packet.getLength());
- #endif
- return payload;
- }
- return 0;
- }
- #if SRT_DEBUG_TRACE_SND
- class snd_logger
- {
- typedef srt::sync::steady_clock steady_clock;
- public:
- snd_logger() {}
- ~snd_logger()
- {
- ScopedLock lck(m_mtx);
- m_fout.close();
- }
- struct
- {
- typedef srt::sync::steady_clock steady_clock;
- long long usElapsed;
- steady_clock::time_point tsNow;
- int usSRTT;
- int usRTTVar;
- int msSndBuffSpan;
- int msTimespanTh;
- int msNextUniqueToSend;
- long long usElapsedLastDrop;
- bool canRexmit;
- int iPktSeqno;
- bool isRetransmitted;
- } state;
- void trace()
- {
- using namespace srt::sync;
- ScopedLock lck(m_mtx);
- create_file();
- m_fout << state.usElapsed << ",";
- m_fout << state.usSRTT << ",";
- m_fout << state.usRTTVar << ",";
- m_fout << state.msSndBuffSpan << ",";
- m_fout << state.msTimespanTh << ",";
- m_fout << state.msNextUniqueToSend << ",";
- m_fout << state.usElapsedLastDrop << ",";
- m_fout << state.canRexmit << ",";
- m_fout << state.iPktSeqno << ',';
- m_fout << state.isRetransmitted << '\n';
- m_fout.flush();
- }
- private:
- void print_header()
- {
- m_fout << "usElapsed,usSRTT,usRTTVar,msSndBuffTimespan,msTimespanTh,msNextUniqueToSend,usDLastDrop,canRexmit,sndPktSeqno,isRexmit";
- m_fout << "\n";
- }
- void create_file()
- {
- if (m_fout.is_open())
- return;
- m_start_time = srt::sync::steady_clock::now();
- std::string str_tnow = srt::sync::FormatTimeSys(m_start_time);
- str_tnow.resize(str_tnow.size() - 7); // remove trailing ' [SYST]' part
- while (str_tnow.find(':') != std::string::npos)
- {
- str_tnow.replace(str_tnow.find(':'), 1, 1, '_');
- }
- const std::string fname = "snd_trace_" + str_tnow + ".csv";
- m_fout.open(fname, std::ofstream::out);
- if (!m_fout)
- std::cerr << "IPE: Failed to open " << fname << "!!!\n";
- print_header();
- }
- private:
- srt::sync::Mutex m_mtx;
- std::ofstream m_fout;
- srt::sync::steady_clock::time_point m_start_time;
- };
- snd_logger g_snd_logger;
- #endif // SRT_DEBUG_TRACE_SND
- void srt::CUDT::setPacketTS(CPacket& p, const time_point& ts)
- {
- enterCS(m_StatsLock);
- const time_point tsStart = m_stats.tsStartTime;
- leaveCS(m_StatsLock);
- p.m_iTimeStamp = makeTS(ts, tsStart);
- }
- void srt::CUDT::setDataPacketTS(CPacket& p, const time_point& ts)
- {
- enterCS(m_StatsLock);
- const time_point tsStart = m_stats.tsStartTime;
- leaveCS(m_StatsLock);
- if (!m_bPeerTsbPd)
- {
- // If TSBPD is disabled, use the current time as the source (timestamp using the sending time).
- p.m_iTimeStamp = makeTS(steady_clock::now(), tsStart);
- return;
- }
- // TODO: Might be better for performance to ensure this condition is always false, and just use SRT_ASSERT here.
- if (ts < tsStart)
- {
- p.m_iTimeStamp = makeTS(steady_clock::now(), tsStart);
- LOGC(qslog.Warn,
- log << CONID() << "setPacketTS: reference time=" << FormatTime(ts)
- << " is in the past towards start time=" << FormatTime(tsStart)
- << " - setting NOW as reference time for the data packet");
- return;
- }
- // Use the provided source time for the timestamp.
- p.m_iTimeStamp = makeTS(ts, tsStart);
- }
- bool srt::CUDT::isRetransmissionAllowed(const time_point& tnow SRT_ATR_UNUSED)
- {
- // Prioritization of original packets only applies to Live CC.
- if (!m_bPeerTLPktDrop || !m_config.bMessageAPI)
- return true;
- // TODO: lock sender buffer?
- const time_point tsNextPacket = m_pSndBuffer->peekNextOriginal();
- #if SRT_DEBUG_TRACE_SND
- const int buffdelay_ms = count_milliseconds(m_pSndBuffer->getBufferingDelay(tnow));
- // If there is a small loss, still better to retransmit. If timespan is already big,
- // then consider sending original packets.
- const int threshold_ms_min = (2 * m_iSRTT + 4 * m_iRTTVar + COMM_SYN_INTERVAL_US) / 1000;
- const int msNextUniqueToSend = count_milliseconds(tnow - tsNextPacket) + m_iPeerTsbPdDelay_ms;
- g_snd_logger.state.tsNow = tnow;
- g_snd_logger.state.usElapsed = count_microseconds(tnow - m_stats.tsStartTime);
- g_snd_logger.state.usSRTT = m_iSRTT;
- g_snd_logger.state.usRTTVar = m_iRTTVar;
- g_snd_logger.state.msSndBuffSpan = buffdelay_ms;
- g_snd_logger.state.msTimespanTh = threshold_ms_min;
- g_snd_logger.state.msNextUniqueToSend = msNextUniqueToSend;
- g_snd_logger.state.usElapsedLastDrop = count_microseconds(tnow - m_tsLastTLDrop);
- g_snd_logger.state.canRexmit = false;
- #endif
- if (tsNextPacket != time_point())
- {
- // Can send original packet, so just send it
- return false;
- }
- #ifdef ENABLE_MAXREXMITBW
- m_SndRexmitRate.addSample(tnow, 0, 0); // Update the estimation.
- const int64_t iRexmitRateBps = m_SndRexmitRate.getRate();
- const int64_t iRexmitRateLimitBps = m_config.llMaxRexmitBW;
- if (iRexmitRateLimitBps >= 0 && iRexmitRateBps > iRexmitRateLimitBps)
- {
- // Too many retransmissions, so don't send anything.
- // TODO: When to wake up next time?
- return false;
- }
- #endif
- #if SRT_DEBUG_TRACE_SND
- g_snd_logger.state.canRexmit = true;
- #endif
- return true;
- }
- bool srt::CUDT::packData(CPacket& w_packet, steady_clock::time_point& w_nexttime, sockaddr_any& w_src_addr)
- {
- int payload = 0;
- bool probe = false;
- bool new_packet_packed = false;
- const steady_clock::time_point enter_time = steady_clock::now();
- w_nexttime = enter_time;
- if (!is_zero(m_tsNextSendTime) && enter_time > m_tsNextSendTime)
- {
- m_tdSendTimeDiff = m_tdSendTimeDiff.load() + (enter_time - m_tsNextSendTime);
- }
- ScopedLock connectguard(m_ConnectionLock);
- // If a closing action is done simultaneously, then
- // m_bOpened should already be false, and it's set
- // just before releasing this lock.
- //
- // If this lock is caught BEFORE the closing could
- // start the dissolving process, this process will
- // not be started until this function is finished.
- if (!m_bOpened)
- return false;
- payload = isRetransmissionAllowed(enter_time)
- ? packLostData((w_packet))
- : 0;
- IF_HEAVY_LOGGING(const char* reason); // The source of the data packet (normal/rexmit/filter)
- if (payload > 0)
- {
- IF_HEAVY_LOGGING(reason = "reXmit");
- }
- else if (m_PacketFilter &&
- m_PacketFilter.packControlPacket(m_iSndCurrSeqNo, m_pCryptoControl->getSndCryptoFlags(), (w_packet)))
- {
- HLOGC(qslog.Debug, log << CONID() << "filter: filter/CTL packet ready - packing instead of data.");
- payload = (int) w_packet.getLength();
- IF_HEAVY_LOGGING(reason = "filter");
- // Stats
- ScopedLock lg(m_StatsLock);
- m_stats.sndr.sentFilterExtra.count(1);
- }
- else
- {
- if (!packUniqueData(w_packet))
- {
- m_tsNextSendTime = steady_clock::time_point();
- m_tdSendTimeDiff = steady_clock::duration();
- return false;
- }
- new_packet_packed = true;
- // every 16 (0xF) packets, a packet pair is sent
- if ((w_packet.m_iSeqNo & PUMASK_SEQNO_PROBE) == 0)
- probe = true;
- payload = (int) w_packet.getLength();
- IF_HEAVY_LOGGING(reason = "normal");
- }
- w_packet.m_iID = m_PeerID; // Set the destination SRT socket ID.
- if (new_packet_packed && m_PacketFilter)
- {
- HLOGC(qslog.Debug, log << CONID() << "filter: Feeding packet for source clip");
- m_PacketFilter.feedSource((w_packet));
- }
- #if ENABLE_HEAVY_LOGGING // Required because of referring to MessageFlagStr()
- HLOGC(qslog.Debug,
- log << CONID() << "packData: " << reason << " packet seq=" << w_packet.m_iSeqNo << " (ACK=" << m_iSndLastAck
- << " ACKDATA=" << m_iSndLastDataAck << " MSG/FLAGS: " << w_packet.MessageFlagStr() << ")");
- #endif
- // Fix keepalive
- m_tsLastSndTime.store(enter_time);
- considerLegacySrtHandshake(steady_clock::time_point());
- // WARNING: TEV_SEND is the only event that is reported from
- // the CSndQueue::worker thread. All others are reported from
- // CRcvQueue::worker. If you connect to this signal, make sure
- // that you are aware of prospective simultaneous access.
- updateCC(TEV_SEND, EventVariant(&w_packet));
- // XXX This was a blocked code also originally in UDT. Probably not required.
- // Left untouched for historical reasons.
- // Might be possible that it was because of that this is send from
- // different thread than the rest of the signals.
- // m_pSndTimeWindow->onPktSent(w_packet.m_iTimeStamp);
- enterCS(m_StatsLock);
- m_stats.sndr.sent.count(payload);
- if (new_packet_packed)
- m_stats.sndr.sentUnique.count(payload);
- leaveCS(m_StatsLock);
- const duration sendint = m_tdSendInterval;
- if (probe)
- {
- // sends out probing packet pair
- m_tsNextSendTime = enter_time;
- // Sending earlier, need to adjust the pace later on.
- m_tdSendTimeDiff = m_tdSendTimeDiff.load() - sendint;
- probe = false;
- }
- else
- {
- #if USE_BUSY_WAITING
- m_tsNextSendTime = enter_time + m_tdSendInterval.load();
- #else
- const duration sendbrw = m_tdSendTimeDiff;
- if (sendbrw >= sendint)
- {
- // Send immediately
- m_tsNextSendTime = enter_time;
- // ATOMIC NOTE: this is the only thread that
- // modifies this field
- m_tdSendTimeDiff = sendbrw - sendint;
- }
- else
- {
- m_tsNextSendTime = enter_time + (sendint - sendbrw);
- m_tdSendTimeDiff = duration();
- }
- #endif
- }
- HLOGC(qslog.Debug, log << "packData: Setting source address: " << m_SourceAddr.str());
- w_src_addr = m_SourceAddr;
- w_nexttime = m_tsNextSendTime;
- return payload >= 0; // XXX shouldn't be > 0 ? == 0 is only when buffer range exceeded.
- }
- bool srt::CUDT::packUniqueData(CPacket& w_packet)
- {
- // Check the congestion/flow window limit
- const int cwnd = std::min(int(m_iFlowWindowSize), int(m_dCongestionWindow));
- const int flightspan = getFlightSpan();
- if (cwnd <= flightspan)
- {
- HLOGC(qslog.Debug,
- log << CONID() << "packUniqueData: CONGESTED: cwnd=min(" << m_iFlowWindowSize << "," << m_dCongestionWindow
- << ")=" << cwnd << " seqlen=(" << m_iSndLastAck << "-" << m_iSndCurrSeqNo << ")=" << flightspan);
- return false;
- }
- // XXX Here it's needed to set kflg to msgno_bitset in the block stored in the
- // send buffer. This should be somehow avoided, the crypto flags should be set
- // together with encrypting, and the packet should be sent as is, when rexmitting.
- // It would be nice to research as to whether CSndBuffer::Block::m_iMsgNoBitset field
- // isn't a useless redundant state copy. If it is, then taking the flags here can be removed.
- const int kflg = m_pCryptoControl->getSndCryptoFlags();
- int pktskipseqno = 0;
- time_point tsOrigin;
- const int pld_size = m_pSndBuffer->readData((w_packet), (tsOrigin), kflg, (pktskipseqno));
- if (pktskipseqno)
- {
- // Some packets were skipped due to TTL expiry.
- m_iSndCurrSeqNo = CSeqNo::incseq(m_iSndCurrSeqNo, pktskipseqno);
- HLOGC(qslog.Debug, log << "packUniqueData: reading skipped " << pktskipseqno << " seq up to %" << m_iSndCurrSeqNo
- << " due to TTL expiry");
- }
- if (pld_size == 0)
- {
- HLOGC(qslog.Debug, log << "packUniqueData: nothing extracted from the buffer");
- return false;
- }
- // A CHANGE. The sequence number is currently added to the packet
- // when scheduling, not when extracting. This is a inter-migration form,
- // only override extraction sequence with scheduling sequence in group mode.
- m_iSndCurrSeqNo = CSeqNo::incseq(m_iSndCurrSeqNo);
- #if ENABLE_BONDING
- // Fortunately the group itself isn't being accessed.
- if (m_parent->m_GroupOf)
- {
- const int packetspan = CSeqNo::seqoff(m_iSndCurrSeqNo, w_packet.m_iSeqNo);
- if (packetspan > 0)
- {
- // After increasing by 1, but being previously set as ISN-1, this should be == ISN,
- // if this is the very first packet to send.
- if (m_iSndCurrSeqNo == m_iISN)
- {
- // This is the very first packet to be sent; so there's nothing in
- // the sending buffer yet, and therefore we are in a situation as just
- // after connection. No packets in the buffer, no packets are sent,
- // no ACK to be awaited. We can screw up all the variables that are
- // initialized from ISN just after connection.
- LOGC(qslog.Note,
- log << CONID() << "packUniqueData: Fixing EXTRACTION sequence " << m_iSndCurrSeqNo
- << " from SCHEDULING sequence " << w_packet.m_iSeqNo << " for the first packet: DIFF="
- << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength()));
- }
- else
- {
- // There will be a serious data discrepancy between the agent and the peer.
- LOGC(qslog.Error,
- log << CONID() << "IPE: packUniqueData: Fixing EXTRACTION sequence " << m_iSndCurrSeqNo
- << " from SCHEDULING sequence " << w_packet.m_iSeqNo << " in the middle of transition: DIFF="
- << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength()));
- }
- // Additionally send the drop request to the peer so that it
- // won't stupidly request the packets to be retransmitted.
- // Don't do it if the difference isn't positive or exceeds the threshold.
- int32_t seqpair[2];
- seqpair[0] = m_iSndCurrSeqNo;
- seqpair[1] = CSeqNo::decseq(w_packet.m_iSeqNo);
- const int32_t no_msgno = 0;
- LOGC(qslog.Debug,
- log << CONID() << "packUniqueData: Sending DROPREQ: SEQ: " << seqpair[0] << " - " << seqpair[1] << " ("
- << packetspan << " packets)");
- sendCtrl(UMSG_DROPREQ, &no_msgno, seqpair, sizeof(seqpair));
- // In case when this message is lost, the peer will still get the
- // UMSG_DROPREQ message when the agent realizes that the requested
- // packet are not present in the buffer (preadte the send buffer).
- // Override extraction sequence with scheduling sequence.
- m_iSndCurrSeqNo = w_packet.m_iSeqNo;
- ScopedLock ackguard(m_RecvAckLock);
- m_iSndLastAck = w_packet.m_iSeqNo;
- m_iSndLastDataAck = w_packet.m_iSeqNo;
- m_iSndLastFullAck = w_packet.m_iSeqNo;
- m_iSndLastAck2 = w_packet.m_iSeqNo;
- }
- else if (packetspan < 0)
- {
- LOGC(qslog.Error,
- log << CONID() << "IPE: packData: SCHEDULING sequence " << w_packet.m_iSeqNo
- << " is behind of EXTRACTION sequence " << m_iSndCurrSeqNo << ", dropping this packet: DIFF="
- << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength()));
- // XXX: Probably also change the socket state to broken?
- return false;
- }
- }
- else
- #endif
- {
- HLOGC(qslog.Debug,
- log << CONID() << "packUniqueData: Applying EXTRACTION sequence " << m_iSndCurrSeqNo
- << " over SCHEDULING sequence " << w_packet.m_iSeqNo << " for socket not in group:"
- << " DIFF=" << CSeqNo::seqcmp(m_iSndCurrSeqNo, w_packet.m_iSeqNo)
- << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength()));
- // Do this always when not in a group.
- w_packet.m_iSeqNo = m_iSndCurrSeqNo;
- }
- // Set missing fields before encrypting the packet, because those fields might be used for encryption.
- w_packet.m_iID = m_PeerID; // Destination SRT Socket ID
- setDataPacketTS(w_packet, tsOrigin);
- if (kflg != EK_NOENC)
- {
- // Note that the packet header must have a valid seqno set, as it is used as a counter for encryption.
- // Other fields of the data packet header (e.g. timestamp, destination socket ID) are not used for the counter.
- // Cypher may change packet length!
- if (m_pCryptoControl->encrypt((w_packet)) != ENCS_CLEAR)
- {
- // Encryption failed
- //>>Add stats for crypto failure
- LOGC(qslog.Warn, log << CONID() << "ENCRYPT FAILED - packet won't be sent, size=" << pld_size);
- return false;
- }
- checkSndKMRefresh();
- }
- #if SRT_DEBUG_TRACE_SND
- g_snd_logger.state.iPktSeqno = w_packet.m_iSeqNo;
- g_snd_logger.state.isRetransmitted = w_packet.getRexmitFlag();
- g_snd_logger.trace();
- #endif
- return true;
- }
- // This is a close request, but called from the
- void srt::CUDT::processClose()
- {
- sendCtrl(UMSG_SHUTDOWN);
- m_bShutdown = true;
- m_bClosing = true;
- m_bBroken = true;
- m_iBrokenCounter = 60;
- HLOGP(smlog.Debug, "processClose: sent message and set flags");
- if (m_bTsbPd)
- {
- HLOGP(smlog.Debug, "processClose: lock-and-signal TSBPD");
- CSync::lock_notify_one(m_RcvTsbPdCond, m_RecvLock);
- }
- // Signal the sender and recver if they are waiting for data.
- releaseSynch();
- // Unblock any call so they learn the connection_broken error
- uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_ERR, true);
- HLOGP(smlog.Debug, "processClose: triggering timer event to spread the bad news");
- CGlobEvent::triggerEvent();
- }
- void srt::CUDT::sendLossReport(const std::vector<std::pair<int32_t, int32_t> > &loss_seqs)
- {
- vector<int32_t> seqbuffer;
- seqbuffer.reserve(2 * loss_seqs.size()); // pessimistic
- for (loss_seqs_t::const_iterator i = loss_seqs.begin(); i != loss_seqs.end(); ++i)
- {
- if (i->first == i->second)
- {
- seqbuffer.push_back(i->first);
- HLOGC(qrlog.Debug, log << "lost packet " << i->first << ": sending LOSSREPORT");
- }
- else
- {
- seqbuffer.push_back(i->first | LOSSDATA_SEQNO_RANGE_FIRST);
- seqbuffer.push_back(i->second);
- HLOGC(qrlog.Debug, log << "lost packets " << i->first << "-" << i->second
- << " (" << (1 + CSeqNo::seqcmp(i->second, i->first)) << " packets): sending LOSSREPORT");
- }
- }
- if (!seqbuffer.empty())
- {
- sendCtrl(UMSG_LOSSREPORT, NULL, &seqbuffer[0], (int) seqbuffer.size());
- }
- }
- bool srt::CUDT::overrideSndSeqNo(int32_t seq)
- {
- // This function is intended to be called from the socket
- // group management functions to synchronize the sequnece in
- // all sockes in the bonding group. THIS sequence given
- // here is the sequence TO BE STAMPED AT THE EXACTLY NEXT
- // sent payload. Therefore, screw up the ISN to exactly this
- // value, and the send sequence to the value one less - because
- // the m_iSndCurrSeqNo is increased by one immediately before
- // stamping it to the packet.
- // This function can only be called:
- // - from the operation on an idle socket in the socket group
- // - IMMEDIATELY after connection established and BEFORE the first payload
- // - The corresponding socket at the peer side must be also
- // in this idle state!
- ScopedLock cg (m_RecvAckLock);
- // Both the scheduling and sending sequences should be fixed.
- // The new sequence normally should jump over several sequence numbers
- // towards what is currently in m_iSndCurrSeqNo.
- // Therefore it's not allowed that:
- // - the jump go backward: backward packets should be already there
- // - the jump go forward by a value larger than half the period: DISCREPANCY.
- const int diff = CSeqNo(seq) - CSeqNo(m_iSndCurrSeqNo);
- if (diff < 0 || diff > CSeqNo::m_iSeqNoTH)
- {
- LOGC(gslog.Error, log << CONID() << "IPE: Overriding with seq %" << seq << " DISCREPANCY against current %"
- << m_iSndCurrSeqNo << " and next sched %" << m_iSndNextSeqNo << " - diff=" << diff);
- return false;
- }
- //
- // The peer will have to do the same, as a reaction on perceived
- // packet loss. When it recognizes that this initial screwing up
- // has happened, it should simply ignore the loss and go on.
- // ISN isn't being changed here - it doesn't make much sense now.
- setInitialSndSeq(seq);
- // m_iSndCurrSeqNo will be most likely lower than m_iSndNextSeqNo because
- // the latter is ahead with the number of packets already scheduled, but
- // not yet sent.
- HLOGC(gslog.Debug,
- log << CONID() << "overrideSndSeqNo: sched-seq=" << m_iSndNextSeqNo << " send-seq=" << m_iSndCurrSeqNo
- << " (unchanged)");
- return true;
- }
- int srt::CUDT::checkLazySpawnTsbPdThread()
- {
- const bool need_tsbpd = m_bTsbPd || m_bGroupTsbPd;
- if (need_tsbpd && !m_RcvTsbPdThread.joinable())
- {
- ScopedLock lock(m_RcvTsbPdStartupLock);
- if (m_bClosing) // Check again to protect join() in CUDT::releaseSync()
- return -1;
- HLOGP(qrlog.Debug, "Spawning Socket TSBPD thread");
- #if ENABLE_HEAVY_LOGGING
- std::ostringstream tns1, tns2;
- // Take the last 2 ciphers from the socket ID.
- tns1 << setfill('0') << setw(2) << m_SocketID;
- std::string s = tns1.str();
- tns2 << "SRT:TsbPd:@" << s.substr(s.size()-2, 2);
- const string thname = tns2.str();
- #else
- const string thname = "SRT:TsbPd";
- #endif
- if (!StartThread(m_RcvTsbPdThread, CUDT::tsbpd, this, thname))
- return -1;
- }
- return 0;
- }
- CUDT::time_point srt::CUDT::getPktTsbPdTime(void*, const CPacket& packet)
- {
- return m_pRcvBuffer->getPktTsbPdTime(packet.getMsgTimeStamp());
- }
- SRT_ATR_UNUSED static const char *const s_rexmitstat_str[] = {"ORIGINAL", "REXMITTED", "RXS-UNKNOWN"};
- // [[using locked(m_RcvBufferLock)]]
- int srt::CUDT::handleSocketPacketReception(const vector<CUnit*>& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs)
- {
- bool excessive SRT_ATR_UNUSED = true; // stays true unless it was successfully added
- w_new_inserted = false;
- const int32_t bufseq = m_pRcvBuffer->getStartSeqNo();
- // Loop over all incoming packets that were filtered out.
- // In case when there is no filter, there's just one packet in 'incoming',
- // the one that came in the input of this function.
- for (vector<CUnit *>::const_iterator unitIt = incoming.begin(); unitIt != incoming.end(); ++unitIt)
- {
- CUnit * u = *unitIt;
- CPacket &rpkt = u->m_Packet;
- const int pktrexmitflag = m_bPeerRexmitFlag ? (rpkt.getRexmitFlag() ? 1 : 0) : 2;
- const bool retransmitted = pktrexmitflag == 1;
- bool adding_successful = true;
- const int32_t bufidx = CSeqNo::seqoff(bufseq, rpkt.m_iSeqNo);
- IF_HEAVY_LOGGING(const char *exc_type = "EXPECTED");
- // bufidx < 0: the packet is in the past for the buffer
- // seqno <% m_iRcvLastAck : the sequence may be within the buffer,
- // but if so, it is in the acknowledged-but-not-retrieved area.
- // NOTE: if we have a situation when there are any packets in the
- // acknowledged area, but they aren't retrieved, this area DOES NOT
- // contain any losses. So a packet in this area is at best a duplicate.
- // In case when a loss would be abandoned (TLPKTDROP), there must at
- // some point happen to be an empty first cell in the buffer, followed
- // somewhere by a valid packet. If this state is achieved at some point,
- // the acknowledgement sequence should be equal to the beginning of the
- // buffer. Then, when TSBPD decides to drop these initial empty cells,
- // we'll have: (m_iRcvLastAck <% buffer->getStartSeqNo()) - and in this
- // case (bufidx < 0) condition will be satisfied also for this case.
- //
- // The only case when bufidx > 0, but packet seq is <% m_iRcvLastAck
- // is when the packet sequence is within the initial contiguous area,
- // which never contains losses, so discarding this packet does not
- // discard a loss coverage, even if this were past ACK.
- if (bufidx < 0 || CSeqNo::seqcmp(rpkt.m_iSeqNo, m_iRcvLastAck) < 0)
- {
- time_point pts = getPktTsbPdTime(NULL, rpkt);
- enterCS(m_StatsLock);
- const double bltime = (double) CountIIR<uint64_t>(
- uint64_t(m_stats.traceBelatedTime) * 1000,
- count_microseconds(steady_clock::now() - pts), 0.2);
- m_stats.traceBelatedTime = bltime / 1000.0;
- m_stats.rcvr.recvdBelated.count(rpkt.getLength());
- leaveCS(m_StatsLock);
- HLOGC(qrlog.Debug,
- log << CONID() << "RECEIVED: %" << rpkt.m_iSeqNo << " bufidx=" << bufidx << " (BELATED/"
- << s_rexmitstat_str[pktrexmitflag] << ") with ACK %" << m_iRcvLastAck
- << " FLAGS: " << rpkt.MessageFlagStr());
- continue;
- }
- if (bufidx >= int(m_pRcvBuffer->capacity()))
- {
- // This is already a sequence discrepancy. Probably there could be found
- // some way to make it continue reception by overriding the sequence and
- // make a kinda TLKPTDROP, but there has been found no reliable way to do this.
- if (m_bTsbPd && m_bTLPktDrop && m_pRcvBuffer->empty())
- {
- // Only in live mode. In File mode this shall not be possible
- // because the sender should stop sending in this situation.
- // In Live mode this means that there is a gap between the
- // lowest sequence in the empty buffer and the incoming sequence
- // that exceeds the buffer size. Receiving data in this situation
- // is no longer possible and this is a point of no return.
- LOGC(qrlog.Error, log << CONID() <<
- "SEQUENCE DISCREPANCY. BREAKING CONNECTION."
- " %" << rpkt.m_iSeqNo
- << " buffer=(%" << bufseq
- << ":%" << m_iRcvCurrSeqNo // -1 = size to last index
- << "+%" << CSeqNo::incseq(bufseq, int(m_pRcvBuffer->capacity()) - 1)
- << "), " << (m_pRcvBuffer->capacity() - bufidx + 1)
- << " past max. Reception no longer possible. REQUESTING TO CLOSE.");
- return -2;
- }
- else
- {
- LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo
- << ", insert offset " << bufidx << ". "
- << m_pRcvBuffer->strFullnessState(m_iRcvLastAck, steady_clock::now())
- );
- return -1;
- }
- }
- const int buffer_add_result = m_pRcvBuffer->insert(u);
- if (buffer_add_result < 0)
- {
- // The insert() result is -1 if at the position evaluated from this packet's
- // sequence number there already is a packet.
- // So this packet is "redundant".
- IF_HEAVY_LOGGING(exc_type = "UNACKED");
- adding_successful = false;
- }
- else
- {
- w_new_inserted = true;
- IF_HEAVY_LOGGING(exc_type = "ACCEPTED");
- excessive = false;
- if (u->m_Packet.getMsgCryptoFlags() != EK_NOENC)
- {
- // TODO: reset and restore the timestamp if TSBPD is disabled.
- // Reset retransmission flag (must be excluded from GCM auth tag).
- u->m_Packet.setRexmitFlag(false);
- const EncryptionStatus rc = m_pCryptoControl ? m_pCryptoControl->decrypt((u->m_Packet)) : ENCS_NOTSUP;
- u->m_Packet.setRexmitFlag(retransmitted); // Recover the flag.
- if (rc != ENCS_CLEAR)
- {
- adding_successful = false;
- IF_HEAVY_LOGGING(exc_type = "UNDECRYPTED");
- // If TSBPD is disabled, then SRT either operates in buffer mode, of in message API without a restriction
- // of a single message packet. In that case just dropping a packet is not enough.
- // In message mode the whole message has to be dropped.
- // However, when decryption fails the message number in the packet cannot be trusted.
- // The packet has to be removed from the RCV buffer based on that pkt sequence number,
- // and the sequence number itself must go into the RCV loss list.
- // See issue ##2626.
- SRT_ASSERT(m_bTsbPd);
- // Drop the packet from the receiver buffer.
- // The packet was added to the buffer based on the sequence number, therefore sequence number should be used to drop it from the buffer.
- // A drawback is that it would prevent a valid packet with the same sequence number, if it happens to arrive later, to end up in the buffer.
- const int iDropCnt = m_pRcvBuffer->dropMessage(u->m_Packet.getSeqNo(), u->m_Packet.getSeqNo(), SRT_MSGNO_NONE, CRcvBuffer::DROP_EXISTING);
- const steady_clock::time_point tnow = steady_clock::now();
- ScopedLock lg(m_StatsLock);
- m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * rpkt.getLength(), iDropCnt));
- m_stats.rcvr.undecrypted.count(stats::BytesPackets(rpkt.getLength(), 1));
- if (frequentLogAllowed(tnow))
- {
- LOGC(qrlog.Warn, log << CONID() << "Decryption failed (seqno %" << u->m_Packet.getSeqNo() << "), dropped "
- << iDropCnt << ". pktRcvUndecryptTotal=" << m_stats.rcvr.undecrypted.total.count() << ".");
- m_tsLogSlowDown = tnow;
- }
- }
- }
- else if (m_pCryptoControl && m_pCryptoControl->getCryptoMode() == CSrtConfig::CIPHER_MODE_AES_GCM)
- {
- // Unencrypted packets are not allowed.
- const int iDropCnt = m_pRcvBuffer->dropMessage(u->m_Packet.getSeqNo(), u->m_Packet.getSeqNo(), SRT_MSGNO_NONE, CRcvBuffer::DROP_EXISTING);
- const steady_clock::time_point tnow = steady_clock::now();
- ScopedLock lg(m_StatsLock);
- m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt* rpkt.getLength(), iDropCnt));
- m_stats.rcvr.undecrypted.count(stats::BytesPackets(rpkt.getLength(), 1));
- if (frequentLogAllowed(tnow))
- {
- LOGC(qrlog.Warn, log << CONID() << "Packet not encrypted (seqno %" << u->m_Packet.getSeqNo() << "), dropped "
- << iDropCnt << ". pktRcvUndecryptTotal=" << m_stats.rcvr.undecrypted.total.count() << ".");
- m_tsLogSlowDown = tnow;
- }
- }
- }
- if (adding_successful)
- {
- ScopedLock statslock(m_StatsLock);
- m_stats.rcvr.recvdUnique.count(u->m_Packet.getLength());
- }
- #if ENABLE_HEAVY_LOGGING
- std::ostringstream expectspec;
- if (excessive)
- expectspec << "EXCESSIVE(" << exc_type << ")";
- else
- expectspec << "ACCEPTED";
- std::ostringstream bufinfo;
- if (m_pRcvBuffer)
- {
- // XXX Fix this when the end of contiguous region detection is added.
- const int ackidx = std::max(0, CSeqNo::seqoff(m_pRcvBuffer->getStartSeqNo(), m_iRcvLastAck));
- bufinfo << " BUF.s=" << m_pRcvBuffer->capacity()
- << " avail=" << (int(m_pRcvBuffer->capacity()) - ackidx)
- << " buffer=(%" << bufseq
- << ":%" << m_iRcvCurrSeqNo // -1 = size to last index
- << "+%" << CSeqNo::incseq(bufseq, int(m_pRcvBuffer->capacity()) - 1)
- << ")";
- }
- // Empty buffer info in case of groupwise receiver.
- // There's no way to obtain this information here.
- LOGC(qrlog.Debug, log << CONID() << "RECEIVED: %" << rpkt.m_iSeqNo
- << bufinfo.str()
- << " RSL=" << expectspec.str()
- << " SN=" << s_rexmitstat_str[pktrexmitflag]
- << " FLAGS: "
- << rpkt.MessageFlagStr());
- #endif
- // Decryption should have made the crypto flags EK_NOENC.
- // Otherwise it's an error.
- if (adding_successful)
- {
- HLOGC(qrlog.Debug,
- log << CONID()
- << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.m_iSeqNo));
- if (CSeqNo::seqcmp(rpkt.m_iSeqNo, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection.
- {
- int32_t seqlo = CSeqNo::incseq(m_iRcvCurrSeqNo);
- int32_t seqhi = CSeqNo::decseq(rpkt.m_iSeqNo);
- w_srt_loss_seqs.push_back(make_pair(seqlo, seqhi));
- HLOGC(qrlog.Debug, log << "pkt/LOSS DETECTED: %" << seqlo << " - %" << seqhi);
- }
- }
- // Update the current largest sequence number that has been received.
- // Or it is a retransmitted packet, remove it from receiver loss list.
- if (CSeqNo::seqcmp(rpkt.m_iSeqNo, m_iRcvCurrSeqNo) > 0)
- {
- m_iRcvCurrSeqNo = rpkt.m_iSeqNo; // Latest possible received
- }
- else
- {
- unlose(rpkt); // was BELATED or RETRANSMITTED
- w_was_sent_in_order &= 0 != pktrexmitflag;
- }
- }
- return 0;
- }
- int srt::CUDT::processData(CUnit* in_unit)
- {
- if (m_bClosing)
- return -1;
- CPacket &packet = in_unit->m_Packet;
- // Just heard from the peer, reset the expiration count.
- m_iEXPCount = 1;
- m_tsLastRspTime.store(steady_clock::now());
- // We are receiving data, start tsbpd thread if TsbPd is enabled
- if (-1 == checkLazySpawnTsbPdThread())
- {
- return -1;
- }
- const int pktrexmitflag = m_bPeerRexmitFlag ? (packet.getRexmitFlag() ? 1 : 0) : 2;
- const bool retransmitted = pktrexmitflag == 1;
- #if ENABLE_HEAVY_LOGGING
- string rexmit_reason;
- #endif
- if (retransmitted)
- {
- // This packet was retransmitted
- enterCS(m_StatsLock);
- m_stats.rcvr.recvdRetrans.count(packet.getLength());
- leaveCS(m_StatsLock);
- #if ENABLE_HEAVY_LOGGING
- // Check if packet was retransmitted on request or on ack timeout
- // Search the sequence in the loss record.
- rexmit_reason = " by ";
- ScopedLock lock(m_RcvLossLock);
- if (!m_pRcvLossList->find(packet.m_iSeqNo, packet.m_iSeqNo))
- rexmit_reason += "BLIND";
- else
- rexmit_reason += "NAKREPORT";
- #endif
- }
- #if ENABLE_HEAVY_LOGGING
- {
- steady_clock::duration tsbpddelay = milliseconds_from(m_iTsbPdDelay_ms); // (value passed to CRcvBuffer::setRcvTsbPdMode)
- // It's easier to remove the latency factor from this value than to add a function
- // that exposes the details basing on which this value is calculated.
- steady_clock::time_point pts = m_pRcvBuffer->getPktTsbPdTime(packet.getMsgTimeStamp());
- steady_clock::time_point ets = pts - tsbpddelay;
- HLOGC(qrlog.Debug, log << CONID() << "processData: RECEIVED DATA: size=" << packet.getLength()
- << " seq=" << packet.getSeqNo()
- // XXX FIX IT. OTS should represent the original sending time, but it's relative.
- //<< " OTS=" << FormatTime(packet.getMsgTimeStamp())
- << " ETS=" << FormatTime(ets)
- << " PTS=" << FormatTime(pts));
- }
- #endif
- updateCC(TEV_RECEIVE, EventVariant(&packet));
- ++m_iPktCount;
- const int pktsz = (int) packet.getLength();
- // Update time information
- // XXX Note that this adds the byte size of a packet
- // of which we don't yet know as to whether this has
- // carried out some useful data or some excessive data
- // that will be later discarded.
- // FIXME: before adding this on the rcv time window,
- // make sure that this packet isn't going to be
- // effectively discarded, as repeated retransmission,
- // for example, burdens the link, but doesn't better the speed.
- m_RcvTimeWindow.onPktArrival(pktsz);
- // Probe the packet pair if needed.
- // Conditions and any extra data required for the packet
- // this function will extract and test as needed.
- const bool unordered = CSeqNo::seqcmp(packet.m_iSeqNo, m_iRcvCurrSeqNo) <= 0;
- // Retransmitted and unordered packets do not provide expected measurement.
- // We expect the 16th and 17th packet to be sent regularly,
- // otherwise measurement must be rejected.
- m_RcvTimeWindow.probeArrival(packet, unordered || retransmitted);
- enterCS(m_StatsLock);
- m_stats.rcvr.recvd.count(pktsz);
- leaveCS(m_StatsLock);
- loss_seqs_t filter_loss_seqs;
- loss_seqs_t srt_loss_seqs;
- vector<CUnit *> incoming;
- bool was_sent_in_order = true;
- // If the peer doesn't understand REXMIT flag, send rexmit request
- // always immediately.
- int initial_loss_ttl = 0;
- if (m_bPeerRexmitFlag)
- initial_loss_ttl = m_iReorderTolerance;
- // Track packet loss in statistics early, because a packet filter (e.g. FEC) might recover it later on,
- // supply the missing packet(s), and the loss will no longer be visible for the code that follows.
- if (packet.getMsgSeq(m_bPeerRexmitFlag) != SRT_MSGNO_CONTROL) // disregard filter-control packets, their seq may mean nothing
- {
- const int diff = CSeqNo::seqoff(m_iRcvCurrPhySeqNo, packet.m_iSeqNo);
- // Difference between these two sequence numbers is expected to be:
- // 0 - duplicated last packet (theory only)
- // 1 - subsequent packet (alright)
- // <0 - belated or recovered packet
- // >1 - jump over a packet loss (loss = seqdiff-1)
- if (diff > 1)
- {
- const int loss = diff - 1; // loss is all that is above diff == 1
- ScopedLock lg(m_StatsLock);
- const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize();
- m_stats.rcvr.lost.count(stats::BytesPackets(loss * avgpayloadsz, (uint32_t) loss));
- HLOGC(qrlog.Debug,
- log << CONID() << "LOSS STATS: n=" << loss << " SEQ: [" << CSeqNo::incseq(m_iRcvCurrPhySeqNo) << " "
- << CSeqNo::decseq(packet.m_iSeqNo) << "]");
- }
- if (diff > 0)
- {
- // Record if it was further than latest
- m_iRcvCurrPhySeqNo = packet.m_iSeqNo;
- }
- }
- // [[using locked()]]; // (NOTHING locked)
- #if ENABLE_BONDING
- // Switch to RUNNING even if there was a discrepancy, unless
- // it was long way forward.
- // XXX Important: This code is in the dead function defaultPacketArrival
- // but normally it should be called here regardless if the packet was
- // accepted or rejected because if it was belated it may result in a
- // "runaway train" problem as the IDLE links are being updated the base
- // reception sequence pointer stating that this link is not receiving.
- if (m_parent->m_GroupOf)
- {
- ScopedLock protect_group_existence (uglobal().m_GlobControlLock);
- groups::SocketData* gi = m_parent->m_GroupMemberData;
- // This check is needed as after getting the lock the socket
- // could be potentially removed. It is however granted that as long
- // as gi is non-NULL iterator, the group does exist and it does contain
- // this socket as member (that is, 'gi' cannot be a dangling pointer).
- if (gi != NULL)
- {
- if (gi->rcvstate < SRT_GST_RUNNING) // PENDING or IDLE, tho PENDING is unlikely
- {
- HLOGC(qrlog.Debug,
- log << CONID() << "processData: IN-GROUP rcv state transition " << srt_log_grp_state[gi->rcvstate]
- << " -> RUNNING.");
- gi->rcvstate = SRT_GST_RUNNING;
- }
- else
- {
- HLOGC(qrlog.Debug,
- log << CONID() << "processData: IN-GROUP rcv state transition NOT DONE - state:"
- << srt_log_grp_state[gi->rcvstate]);
- }
- }
- }
- #endif
- bool new_inserted = false;
- if (m_PacketFilter)
- {
- // Stuff this data into the filter
- m_PacketFilter.receive(in_unit, (incoming), (filter_loss_seqs));
- HLOGC(qrlog.Debug,
- log << CONID() << "(FILTER) fed data, received " << incoming.size() << " pkts, " << Printable(filter_loss_seqs)
- << " loss to report, "
- << (m_PktFilterRexmitLevel == SRT_ARQ_ALWAYS ? "FIND & REPORT LOSSES YOURSELF"
- : "REPORT ONLY THOSE"));
- }
- else
- {
- // Stuff in just one packet that has come in.
- incoming.push_back(in_unit);
- }
- {
- // Start of offset protected section
- // Prevent TsbPd thread from modifying Ack position while adding data
- // offset from RcvLastAck in RcvBuffer must remain valid between seqoff() and addData()
- UniqueLock recvbuf_acklock(m_RcvBufferLock);
- // Needed for possibly check for needsQuickACK.
- const bool incoming_belated = (CSeqNo::seqcmp(in_unit->m_Packet.m_iSeqNo, m_pRcvBuffer->getStartSeqNo()) < 0);
- const int res = handleSocketPacketReception(incoming,
- (new_inserted),
- (was_sent_in_order),
- (srt_loss_seqs));
- if (res == -2)
- {
- // This is a scoped lock with AckLock, but for the moment
- // when processClose() is called this lock must be taken out,
- // otherwise this will cause a deadlock. We don't need this
- // lock anymore, and at 'return' it will be unlocked anyway.
- recvbuf_acklock.unlock();
- processClose();
- return -1;
- }
- if (res == -1)
- {
- return -1;
- }
- if (!srt_loss_seqs.empty())
- {
- ScopedLock lock(m_RcvLossLock);
- HLOGC(qrlog.Debug,
- log << CONID() << "processData: RECORDING LOSS: " << Printable(srt_loss_seqs)
- << " tolerance=" << initial_loss_ttl);
- for (loss_seqs_t::iterator i = srt_loss_seqs.begin(); i != srt_loss_seqs.end(); ++i)
- {
- m_pRcvLossList->insert(i->first, i->second);
- if (initial_loss_ttl)
- {
- // The LOSSREPORT will be sent after initial_loss_ttl.
- m_FreshLoss.push_back(CRcvFreshLoss(i->first, i->second, initial_loss_ttl));
- }
- }
- }
- // This is moved earlier after introducing filter because it shouldn't
- // be executed in case when the packet was rejected by the receiver buffer.
- // However now the 'excessive' condition may be true also in case when
- // a truly non-excessive packet has been received, just it has been temporarily
- // stored for better times by the filter module. This way 'excessive' is also true,
- // although the old condition that a packet with a newer sequence number has arrived
- // or arrived out of order may still be satisfied.
- if (!incoming_belated && was_sent_in_order)
- {
- // Basing on some special case in the packet, it might be required
- // to enforce sending ACK immediately (earlier than normally after
- // a given period).
- if (m_CongCtl->needsQuickACK(packet))
- {
- m_tsNextACKTime.store(steady_clock::now());
- }
- }
- if (!new_inserted)
- {
- return -1;
- }
- } // End of recvbuf_acklock
- if (m_bClosing)
- {
- // RcvQueue worker thread can call processData while closing (or close while processData)
- // This race condition exists in the UDT design but the protection against TsbPd thread
- // (with AckLock) and decryption enlarged the probability window.
- // Application can crash deep in decrypt stack since crypto context is deleted in close.
- // RcvQueue worker thread will not necessarily be deleted with this connection as it can be
- // used by others (socket multiplexer).
- return -1;
- }
- if (incoming.empty())
- {
- // Treat as excessive. This is when a filter cumulates packets
- // until the loss is rebuilt, or eats up a filter control packet
- return -1;
- }
- if (!srt_loss_seqs.empty())
- {
- const bool report_recorded_loss = !m_PacketFilter || m_PktFilterRexmitLevel == SRT_ARQ_ALWAYS;
- if (!initial_loss_ttl && report_recorded_loss)
- {
- HLOGC(qrlog.Debug, log << CONID() << "WILL REPORT LOSSES (SRT): " << Printable(srt_loss_seqs));
- sendLossReport(srt_loss_seqs);
- }
- if (m_bTsbPd)
- {
- HLOGC(qrlog.Debug, log << CONID() << "loss: signaling TSBPD cond");
- CSync::lock_notify_one(m_RcvTsbPdCond, m_RecvLock);
- }
- else
- {
- HLOGC(qrlog.Debug, log << CONID() << "loss: socket is not TSBPD, not signaling");
- }
- }
- // Separately report loss records of those reported by a filter.
- // ALWAYS report whatever has been reported back by a filter. Note that
- // the filter never reports anything when rexmit fallback level is ALWAYS or NEVER.
- // With ALWAYS only those are reported that were recorded here by SRT.
- // With NEVER, nothing is to be reported.
- if (!filter_loss_seqs.empty())
- {
- HLOGC(qrlog.Debug, log << CONID() << "WILL REPORT LOSSES (filter): " << Printable(filter_loss_seqs));
- sendLossReport(filter_loss_seqs);
- if (m_bTsbPd)
- {
- HLOGC(qrlog.Debug, log << CONID() << "loss: signaling TSBPD cond");
- CSync::lock_notify_one(m_RcvTsbPdCond, m_RecvLock);
- }
- }
- // Now review the list of FreshLoss to see if there's any "old enough" to send UMSG_LOSSREPORT to it.
- // PERFORMANCE CONSIDERATIONS:
- // This list is quite inefficient as a data type and finding the candidate to send UMSG_LOSSREPORT
- // is linear time. On the other hand, there are some special cases that are important for performance:
- // - only the first (plus some following) could have had TTL drown to 0
- // - the only (little likely) possibility that the next-to-first record has TTL=0 is when there was
- // a loss range split (due to dropFromLossLists() of one sequence)
- // - first found record with TTL>0 means end of "ready to LOSSREPORT" records
- // So:
- // All you have to do is:
- // - start with first element and continue with next elements, as long as they have TTL=0
- // If so, send the loss report and remove this element.
- // - Since the first element that has TTL>0, iterate until the end of container and decrease TTL.
- //
- // This will be efficient becase the loop to increment one field (without any condition check)
- // can be quite well optimized.
- vector<int32_t> lossdata;
- {
- ScopedLock lg(m_RcvLossLock);
- // XXX There was a mysterious crash around m_FreshLoss. When the initial_loss_ttl is 0
- // (that is, "belated loss report" feature is off), don't even touch m_FreshLoss.
- if (initial_loss_ttl && !m_FreshLoss.empty())
- {
- deque<CRcvFreshLoss>::iterator i = m_FreshLoss.begin();
- // Phase 1: take while TTL <= 0.
- // There can be more than one record with the same TTL, if it has happened before
- // that there was an 'unlost' (@c dropFromLossLists) sequence that has split one detected loss
- // into two records.
- for (; i != m_FreshLoss.end() && i->ttl <= 0; ++i)
- {
- HLOGC(qrlog.Debug, log << "Packet seq " << i->seq[0] << "-" << i->seq[1]
- << " (" << (CSeqNo::seqoff(i->seq[0], i->seq[1]) + 1) << " packets) considered lost - sending LOSSREPORT");
- addLossRecord(lossdata, i->seq[0], i->seq[1]);
- }
- // Remove elements that have been processed and prepared for lossreport.
- if (i != m_FreshLoss.begin())
- {
- m_FreshLoss.erase(m_FreshLoss.begin(), i);
- i = m_FreshLoss.begin();
- }
- if (m_FreshLoss.empty())
- {
- HLOGP(qrlog.Debug, "NO MORE FRESH LOSS RECORDS.");
- }
- else
- {
- HLOGC(qrlog.Debug, log << "STILL " << m_FreshLoss.size() << " FRESH LOSS RECORDS, FIRST: "
- << i->seq[0] << "-" << i->seq[1]
- << " (" << (1 + CSeqNo::seqoff(i->seq[0], i->seq[1])) << ") TTL: " << i->ttl);
- }
- // Phase 2: rest of the records should have TTL decreased.
- for (; i != m_FreshLoss.end(); ++i)
- --i->ttl;
- }
- }
- if (!lossdata.empty())
- {
- sendCtrl(UMSG_LOSSREPORT, NULL, &lossdata[0], (int) lossdata.size());
- }
- // was_sent_in_order means either of:
- // - packet was sent in order (first if branch above)
- // - packet was sent as old, but was a retransmitted packet
- if (m_bPeerRexmitFlag && was_sent_in_order)
- {
- ++m_iConsecOrderedDelivery;
- if (m_iConsecOrderedDelivery >= 50)
- {
- m_iConsecOrderedDelivery = 0;
- if (m_iReorderTolerance > 0)
- {
- m_iReorderTolerance--;
- enterCS(m_StatsLock);
- m_stats.traceReorderDistance--;
- leaveCS(m_StatsLock);
- HLOGC(qrlog.Debug, log << "ORDERED DELIVERY of 50 packets in a row - decreasing tolerance to "
- << m_iReorderTolerance);
- }
- }
- }
- return 0;
- }
- #if ENABLE_BONDING
- // NOTE: this is updated from the value of m_iRcvLastAck,
- // which might be past the buffer and potentially cause setting
- // the value to the last received and re-requiring retransmission.
- // Worst case is that there could be a few packets to tear the transmission
- // even more (as there will be likely no time to recover them), but
- // if the transmission was already torn in the previously active link
- // this shouldn't be a problem that these packets won't be recovered
- // after activating the second link, although will be retried this way.
- void srt::CUDT::updateIdleLinkFrom(CUDT* source)
- {
- int bufseq;
- {
- ScopedLock lg (m_RcvBufferLock);
- bufseq = source->m_pRcvBuffer->getStartSeqNo();
- }
- ScopedLock lg (m_RecvLock);
- if (!m_pRcvBuffer->empty())
- {
- HLOGC(grlog.Debug, log << "grp: NOT updating rcv-seq in @" << m_SocketID << ": receiver buffer not empty");
- return;
- }
- int32_t new_last_rcv = source->m_iRcvLastAck;
- if (CSeqNo::seqcmp(new_last_rcv, bufseq) < 0)
- {
- // Emergency check whether the last ACK was behind the
- // buffer. This may happen when TSBPD dropped empty cells.
- // This may cause that the newly activated link may derive
- // these empty cells which will never be recovered.
- new_last_rcv = bufseq;
- }
- // if (new_last_rcv <=% m_iRcvCurrSeqNo)
- if (CSeqNo::seqcmp(new_last_rcv, m_iRcvCurrSeqNo) <= 0)
- {
- // Reject the change because that would shift the reception pointer backwards.
- HLOGC(grlog.Debug, log << "grp: NOT updating rcv-seq in @" << m_SocketID
- << ": backward setting rejected: %" << m_iRcvCurrSeqNo
- << " -> %" << new_last_rcv);
- return;
- }
- HLOGC(grlog.Debug, log << "grp: updating rcv-seq in @" << m_SocketID
- << " from @" << source->m_SocketID << ": %" << new_last_rcv);
- setInitialRcvSeq(new_last_rcv);
- }
- #endif
- /// This function is called when a packet has arrived, which was behind the current
- /// received sequence - that is, belated or retransmitted. Try to remove the packet
- /// from both loss records: the general loss record and the fresh loss record.
- ///
- /// Additionally, check - if supported by the peer - whether the "latecoming" packet
- /// has been sent due to retransmission or due to reordering, by checking the rexmit
- /// support flag and rexmit flag itself. If this packet was surely ORIGINALLY SENT
- /// it means that the current network connection suffers of packet reordering. This
- /// way try to introduce a dynamic tolerance by calculating the difference between
- /// the current packet reception sequence and this packet's sequence. This value
- /// will be set to the tolerance value, which means that later packet retransmission
- /// will not be required immediately, but only after receiving N next packets that
- /// do not include the lacking packet.
- /// The tolerance is not increased infinitely - it's bordered by iMaxReorderTolerance.
- /// This value can be set in options - SRT_LOSSMAXTTL.
- void srt::CUDT::unlose(const CPacket &packet)
- {
- ScopedLock lg(m_RcvLossLock);
- int32_t sequence = packet.m_iSeqNo;
- m_pRcvLossList->remove(sequence);
- // Rest of this code concerns only the "belated lossreport" feature.
- bool has_increased_tolerance = false;
- bool was_reordered = false;
- if (m_bPeerRexmitFlag)
- {
- // If the peer understands the REXMIT flag, it means that the REXMIT flag is contained
- // in the PH_MSGNO field.
- // The packet is considered coming originally (just possibly out of order), if REXMIT
- // flag is NOT set.
- was_reordered = !packet.getRexmitFlag();
- if (was_reordered)
- {
- HLOGC(qrlog.Debug, log << "received out-of-band packet %" << sequence);
- const int seqdiff = abs(CSeqNo::seqcmp(m_iRcvCurrSeqNo, packet.m_iSeqNo));
- enterCS(m_StatsLock);
- m_stats.traceReorderDistance = max(seqdiff, m_stats.traceReorderDistance);
- leaveCS(m_StatsLock);
- if (seqdiff > m_iReorderTolerance)
- {
- const int new_tolerance = min(seqdiff, m_config.iMaxReorderTolerance);
- HLOGC(qrlog.Debug, log << "Belated by " << seqdiff << " seqs - Reorder tolerance "
- << (new_tolerance == m_iReorderTolerance ? "REMAINS with " : "increased to ") << new_tolerance);
- m_iReorderTolerance = new_tolerance;
- has_increased_tolerance =
- true; // Yes, even if reorder tolerance is already at maximum - this prevents decreasing tolerance.
- }
- }
- else
- {
- HLOGC(qrlog.Debug, log << CONID() << "received reXmitted packet seq=" << sequence);
- }
- }
- else
- {
- HLOGC(qrlog.Debug, log << "received reXmitted or belated packet seq " << sequence << " (distinction not supported by peer)");
- }
- // Don't do anything if "belated loss report" feature is not used.
- // In that case the FreshLoss list isn't being filled in at all, the
- // loss report is sent directly.
- // Note that this condition blocks two things being done in this function:
- // - remove given sequence from the fresh loss record
- // (in this case it's empty anyway)
- // - decrease current reorder tolerance based on whether packets come in order
- // (current reorder tolerance is 0 anyway)
- if (m_bPeerRexmitFlag == 0 || m_iReorderTolerance == 0)
- return;
- int had_ttl = 0;
- if (CRcvFreshLoss::removeOne((m_FreshLoss), sequence, (&had_ttl)))
- {
- HLOGC(qrlog.Debug, log << "sequence " << sequence << " removed from belated lossreport record");
- }
- if (was_reordered)
- {
- m_iConsecOrderedDelivery = 0;
- if (has_increased_tolerance)
- {
- m_iConsecEarlyDelivery = 0; // reset counter
- }
- else if (had_ttl > 2)
- {
- ++m_iConsecEarlyDelivery; // otherwise, and if it arrived quite earlier, increase counter
- HLOGC(qrlog.Debug, log << "... arrived at TTL " << had_ttl << " case " << m_iConsecEarlyDelivery);
- // After 10 consecutive
- if (m_iConsecEarlyDelivery >= 10)
- {
- m_iConsecEarlyDelivery = 0;
- if (m_iReorderTolerance > 0)
- {
- m_iReorderTolerance--;
- enterCS(m_StatsLock);
- m_stats.traceReorderDistance--;
- leaveCS(m_StatsLock);
- HLOGC(qrlog.Debug, log << "... reached " << m_iConsecEarlyDelivery
- << " times - decreasing tolerance to " << m_iReorderTolerance);
- }
- }
- }
- // If hasn't increased tolerance, but the packet appeared at TTL less than 2, do nothing.
- }
- }
- void srt::CUDT::dropFromLossLists(int32_t from, int32_t to)
- {
- ScopedLock lg(m_RcvLossLock);
- IF_HEAVY_LOGGING(bool autodetected = false);
- int32_t begin SRT_ATR_UNUSED;
- if (from == SRT_SEQNO_NONE)
- {
- begin = m_pRcvLossList->removeUpTo(to);
- IF_HEAVY_LOGGING(autodetected = true);
- }
- else
- {
- begin = from;
- m_pRcvLossList->remove(from, to);
- }
- #if ENABLE_HEAVY_LOGGING
- ostringstream range;
- if (begin == SRT_SEQNO_NONE)
- {
- range << "no";
- }
- else
- {
- int off = CSeqNo::seqoff(begin, to);
- if (off < 0)
- {
- range << "WEIRD NUMBER OF";
- }
- else
- {
- range << (off + 1);
- }
- }
- static const char* const beginwhere[2] = {"explicit", "detected"};
- const char* const reqtype = (from == SRT_SEQNO_NONE) ? "TLPKTDROP" : "DROPREQ";
- HLOGC(qrlog.Debug, log << CONID() << "DROP PER " << reqtype << " %" << begin
- << "[" << beginwhere[1*autodetected] << "]-" << to << " ("
- << range.str() << " packets)");
- #endif
- if (m_bPeerRexmitFlag == 0 || m_iReorderTolerance == 0)
- return;
- // All code below concerns only "belated lossreport" feature.
- // It's highly unlikely that this is waiting to send a belated UMSG_LOSSREPORT,
- // so treat it rather as a sanity check.
- // It's enough to check if the first element of the list starts with a sequence older than 'to'.
- // If not, just do nothing.
- size_t delete_index = 0;
- for (size_t i = 0; i < m_FreshLoss.size(); ++i)
- {
- CRcvFreshLoss::Emod result = m_FreshLoss[i].revoke(from, to);
- switch (result)
- {
- case CRcvFreshLoss::DELETE:
- delete_index = i + 1; // PAST THE END
- continue; // There may be further ranges that are included in this one, so check on.
- case CRcvFreshLoss::NONE:
- case CRcvFreshLoss::STRIPPED:
- break; // THIS BREAKS ONLY 'switch', not 'for'!
- case CRcvFreshLoss::SPLIT:; // This function never returns it. It's only a compiler shut-up.
- }
- break; // Now this breaks also FOR.
- }
- m_FreshLoss.erase(m_FreshLoss.begin(),
- m_FreshLoss.begin() + delete_index); // with delete_index == 0 will do nothing
- }
- // This function, as the name states, should bake a new cookie.
- int32_t srt::CUDT::bake(const sockaddr_any& addr, int32_t current_cookie, int correction)
- {
- static unsigned int distractor = 0;
- unsigned int rollover = distractor + 10;
- for (;;)
- {
- // SYN cookie
- char clienthost[NI_MAXHOST];
- char clientport[NI_MAXSERV];
- getnameinfo(addr.get(),
- addr.size(),
- clienthost,
- sizeof(clienthost),
- clientport,
- sizeof(clientport),
- NI_NUMERICHOST | NI_NUMERICSERV);
- int64_t timestamp = (count_microseconds(steady_clock::now() - m_stats.tsStartTime) / 60000000) + distractor +
- correction; // secret changes every one minute
- stringstream cookiestr;
- cookiestr << clienthost << ":" << clientport << ":" << timestamp;
- union {
- unsigned char cookie[16];
- int32_t cookie_val;
- };
- CMD5::compute(cookiestr.str().c_str(), cookie);
- if (cookie_val != current_cookie)
- return cookie_val;
- ++distractor;
- // This is just to make the loop formally breakable,
- // but this is virtually impossible to happen.
- if (distractor == rollover)
- return cookie_val;
- }
- }
- // XXX This is quite a mystery, why this function has a return value
- // and what the purpose for it was. There's just one call of this
- // function in the whole code and in that call the return value is
- // ignored. Actually this call happens in the CRcvQueue::worker thread,
- // where it makes a response for incoming UDP packet that might be
- // a connection request. Should any error occur in this process, there
- // is no way to "report error" that happened here. Basing on that
- // these values in original UDT code were quite like the values
- // for m_iReqType, they have been changed to URQ_* symbols, which
- // may mean that the intent for the return value was to send this
- // value back as a control packet back to the connector.
- //
- // This function is run when the CRcvQueue object is reading packets
- // from the multiplexer (@c CRcvQueue::worker_RetrieveUnit) and the
- // target socket ID is 0.
- //
- // XXX Make this function return EConnectStatus enum type (extend if needed),
- // and this will be directly passed to the caller.
- // [[using locked(m_pRcvQueue->m_LSLock)]];
- int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet)
- {
- // XXX ASSUMPTIONS:
- // [[using assert(packet.m_iID == 0)]]
- HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: received a connection request");
- if (m_bClosing)
- {
- m_RejectReason = SRT_REJ_CLOSE;
- HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... NOT. Rejecting because closing.");
- return m_RejectReason;
- }
- /*
- * Closing a listening socket only set bBroken
- * If a connect packet is received while closing it gets through
- * processing and crashes later.
- */
- if (m_bBroken)
- {
- m_RejectReason = SRT_REJ_CLOSE;
- HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... NOT. Rejecting because broken.");
- return m_RejectReason;
- }
- // When CHandShake::m_iContentSize is used in log, the file fails to link!
- size_t exp_len = CHandShake::m_iContentSize;
- // NOTE!!! Old version of SRT code checks if the size of the HS packet
- // is EQUAL to the above CHandShake::m_iContentSize.
- // Changed to < exp_len because we actually need that the packet
- // be at least of a size for handshake, although it may contain
- // more data, depending on what's inside.
- if (packet.getLength() < exp_len)
- {
- m_RejectReason = SRT_REJ_ROGUE;
- HLOGC(cnlog.Debug,
- log << CONID() << "processConnectRequest: ... NOT. Wrong size: " << packet.getLength()
- << " (expected: " << exp_len << ")");
- return m_RejectReason;
- }
- // Dunno why the original UDT4 code only MUCH LATER was checking if the packet was UMSG_HANDSHAKE.
- // It doesn't seem to make sense to deserialize it into the handshake structure if we are not
- // sure that the packet contains the handshake at all!
- if (!packet.isControl(UMSG_HANDSHAKE))
- {
- m_RejectReason = SRT_REJ_ROGUE;
- LOGC(cnlog.Error,
- log << CONID() << "processConnectRequest: the packet received as handshake is not a handshake message");
- return m_RejectReason;
- }
- CHandShake hs;
- hs.load_from(packet.m_pcData, packet.getLength());
- // XXX MOST LIKELY this hs should be now copied into m_ConnRes field, which holds
- // the handshake structure sent from the peer (no matter the role or mode).
- // This should simplify the createSrtHandshake() function which can this time
- // simply write the crafted handshake structure into m_ConnReq, which needs no
- // participation of the local handshake and passing it as a parameter through
- // newConnection() -> acceptAndRespond() -> createSrtHandshake(). This is also
- // required as a source of the peer's information used in processing in other
- // structures.
- int32_t cookie_val = bake(addr);
- HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: new cookie: " << hex << cookie_val);
- // Remember the incoming destination address here and use it as a source
- // address when responding. It's not possible to record this address yet
- // because this happens still in the frames of the listener socket. Only
- // when processing switches to the newly spawned accepted socket can the
- // address be recorded in its m_SourceAddr field.
- sockaddr_any use_source_addr = packet.udpDestAddr();
- // REQUEST:INDUCTION.
- // Set a cookie, a target ID, and send back the same as
- // RESPONSE:INDUCTION.
- if (hs.m_iReqType == URQ_INDUCTION)
- {
- HLOGC(cnlog.Debug,
- log << CONID() << "processConnectRequest: received type=induction, sending back with cookie+socket");
- // XXX That looks weird - the calculated md5 sum out of the given host/port/timestamp
- // is 16 bytes long, but CHandShake::m_iCookie has 4 bytes. This then effectively copies
- // only the first 4 bytes. Moreover, it's dangerous on some platforms because the char
- // array need not be aligned to int32_t - changed to union in a hope that using int32_t
- // inside a union will enforce whole union to be aligned to int32_t.
- hs.m_iCookie = cookie_val;
- packet.m_iID = hs.m_iID;
- // Ok, now's the time. The listener sets here the version 5 handshake,
- // even though the request was 4. This is because the old client would
- // simply return THE SAME version, not even looking into it, giving the
- // listener false impression as if it supported version 5.
- //
- // If the caller was really HSv4, it will simply ignore the version 5 in INDUCTION;
- // it will respond with CONCLUSION, but with its own set version, which is version 4.
- //
- // If the caller was really HSv5, it will RECOGNIZE this version 5 in INDUCTION, so
- // it will respond with version 5 when sending CONCLUSION.
- hs.m_iVersion = HS_VERSION_SRT1;
- // Additionally, set this field to a MAGIC value. This field isn't used during INDUCTION
- // by HSv4 client, HSv5 client can use it to additionally verify that this is a HSv5 listener.
- // In this field we also advertise the PBKEYLEN value. When 0, it's considered not advertised.
- hs.m_iType = SrtHSRequest::wrapFlags(true /*put SRT_MAGIC_CODE in HSFLAGS*/, m_config.iSndCryptoKeyLen);
- bool whether SRT_ATR_UNUSED = m_config.iSndCryptoKeyLen != 0;
- HLOGC(cnlog.Debug,
- log << CONID() << "processConnectRequest: " << (whether ? "" : "NOT ")
- << " Advertising PBKEYLEN - value = " << m_config.iSndCryptoKeyLen);
- size_t size = packet.getLength();
- hs.store_to((packet.m_pcData), (size));
- setPacketTS(packet, steady_clock::now());
- // Display the HS before sending it to peer
- HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (i): " << hs.show());
- m_pSndQueue->sendto(addr, packet, use_source_addr);
- return SRT_REJ_UNKNOWN; // EXCEPTION: this is a "no-error" code.
- }
- // Otherwise this should be REQUEST:CONCLUSION.
- // Should then come with the correct cookie that was
- // set in the above INDUCTION, in the HS_VERSION_SRT1
- // should also contain extra data.
- if (!hs.valid())
- {
- LOGC(cnlog.Error, log << CONID() << "processConnectRequest: ROGUE HS RECEIVED. Rejecting");
- m_RejectReason = SRT_REJ_ROGUE;
- return SRT_REJ_ROGUE;
- }
- HLOGC(cnlog.Debug,
- log << CONID() << "processConnectRequest: received type=" << RequestTypeStr(hs.m_iReqType)
- << " - checking cookie...");
- if (hs.m_iCookie != cookie_val)
- {
- cookie_val = bake(addr, cookie_val, -1); // SHOULD generate an earlier, distracted cookie
- if (hs.m_iCookie != cookie_val)
- {
- m_RejectReason = SRT_REJ_RDVCOOKIE;
- HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ...wrong cookie " << hex << cookie_val << ". Ignoring.");
- return m_RejectReason;
- }
- HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... correct (FIXED) cookie. Proceeding.");
- }
- else
- {
- HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... correct (ORIGINAL) cookie. Proceeding.");
- }
- int32_t id = hs.m_iID;
- // HANDSHAKE: The old client sees the version that does not match HS_VERSION_UDT4 (5).
- // In this case it will respond with URQ_ERROR_REJECT. Rest of the data are the same
- // as in the handshake request. When this message is received, the connector side should
- // switch itself to the version number HS_VERSION_UDT4 and continue the old way (that is,
- // continue sending URQ_INDUCTION, but this time with HS_VERSION_UDT4).
- bool accepted_hs = true;
- if (hs.m_iVersion == HS_VERSION_SRT1)
- {
- // No further check required.
- // The m_iType contains handshake extension flags.
- }
- else if (hs.m_iVersion == HS_VERSION_UDT4)
- {
- // In UDT, and so in older SRT version, the hs.m_iType field should contain
- // the socket type, although SRT only allowed this field to be UDT_DGRAM.
- // Older SRT version contained that value in a field, but now that this can
- // only contain UDT_DGRAM the field itself has been abandoned.
- // For the sake of any old client that reports version 4 handshake, interpret
- // this hs.m_iType field as a socket type and check if it's UDT_DGRAM.
- // Note that in HSv5 hs.m_iType contains extension flags.
- if (hs.m_iType != UDT_DGRAM)
- {
- m_RejectReason = SRT_REJ_ROGUE;
- accepted_hs = false;
- }
- }
- else
- {
- // Unsupported version
- // (NOTE: This includes "version=0" which is a rejection flag).
- m_RejectReason = SRT_REJ_VERSION;
- accepted_hs = false;
- }
- if (!accepted_hs)
- {
- HLOGC(cnlog.Debug,
- log << CONID() << "processConnectRequest: version/type mismatch. Sending REJECT code:" << m_RejectReason
- << " MSG: " << srt_rejectreason_str(m_RejectReason));
- // mismatch, reject the request
- hs.m_iReqType = URQFailure(m_RejectReason);
- size_t size = CHandShake::m_iContentSize;
- hs.store_to((packet.m_pcData), (size));
- packet.m_iID = id;
- setPacketTS(packet, steady_clock::now());
- HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (e): " << hs.show());
- m_pSndQueue->sendto(addr, packet, use_source_addr);
- }
- else
- {
- // IMPORTANT!!!
- // If the newConnection() detects there is already a socket connection associated with the remote peer,
- // it returns the socket via `acpu`, and the `result` returned is 0.
- // Else if a new connection is successfully created, the conclusion handshake response
- // is sent by the function itself (it calls the acceptAndRespond(..)), the `acpu` remains null, the `result` is 1.
- int error = SRT_REJ_UNKNOWN;
- CUDT* acpu = NULL;
- int result = uglobal().newConnection(m_SocketID, addr, packet, (hs), (error), (acpu));
- // This is listener - m_RejectReason need not be set
- // because listener has no functionality of giving the app
- // insight into rejected callers.
- // --->
- // (global.) CUDTUnited::updateListenerMux
- // (new Socket.) CUDT::acceptAndRespond
- if (result == -1)
- {
- hs.m_iReqType = URQFailure(error);
- LOGC(cnlog.Warn, log << "processConnectRequest: rsp(REJECT): " << hs.m_iReqType << " - " << srt_rejectreason_str(error));
- }
- // The `acpu` not NULL means connection exists, the `result` should be 0. It is not checked here though.
- // The `newConnection(..)` only sends response for newly created connection.
- // The connection already exists (no new connection has been created, no response sent).
- // Send the conclusion response manually here in case the peer has missed the first one.
- // The value `result` here should be 0.
- if (acpu)
- {
- // This is an existing connection, so the handshake is only needed
- // because of the rule that every handshake request must be covered
- // by the handshake response. It wouldn't be good to call interpretSrtHandshake
- // here because the data from the handshake have been already interpreted
- // and recorded. We just need to craft a response.
- HLOGC(cnlog.Debug,
- log << CONID() << "processConnectRequest: sending REPEATED handshake response req="
- << RequestTypeStr(hs.m_iReqType));
- // Rewrite already updated previously data in acceptAndRespond
- acpu->rewriteHandshakeData(acpu->m_PeerAddr, (hs));
- uint32_t kmdata[SRTDATA_MAXSIZE];
- size_t kmdatasize = SRTDATA_MAXSIZE;
- EConnectStatus conn = CONN_ACCEPT;
- if (hs.m_iVersion >= HS_VERSION_SRT1)
- {
- // Always attach extension.
- hs.m_extension = true;
- conn = acpu->craftKmResponse((kmdata), (kmdatasize));
- }
- else
- {
- kmdatasize = 0;
- }
- if (conn != CONN_ACCEPT)
- return conn;
- packet.setLength(m_iMaxSRTPayloadSize);
- if (!acpu->createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP,
- kmdata, kmdatasize,
- (packet), (hs)))
- {
- HLOGC(cnlog.Debug,
- log << CONID() << "processConnectRequest: rejecting due to problems in createSrtHandshake.");
- result = -1; // enforce fallthrough for the below condition!
- hs.m_iReqType = URQFailure(m_RejectReason == SRT_REJ_UNKNOWN ? int(SRT_REJ_IPE) : m_RejectReason.load());
- }
- else
- {
- // Send the crafted handshake
- HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING (repeated) HS (a): " << hs.show());
- acpu->addressAndSend((packet));
- }
- }
- if (result == 1)
- {
- // BUG! There is no need to update write-readiness on the listener socket once new connection is accepted.
- // Only read-readiness has to be updated, but it is done so in the newConnection(..) function.
- // See PR #1831 and issue #1667.
- HLOGC(cnlog.Debug,
- log << CONID() << "processConnectRequest: accepted connection, updating epoll to write-ready");
- // New connection has been accepted or an existing one has been found. Update epoll write-readiness.
- // a new connection has been created, enable epoll for write
- // Note: not using SRT_EPOLL_CONNECT symbol because this is a procedure
- // executed for the accepted socket.
- uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, true);
- }
- else if (result == -1)
- {
- // The new connection failed
- // or the connection already existed, but manually sending the HS response above has failed.
- // HSv4: Send the SHUTDOWN message to the peer (see PR #2010) in order to disallow the peer to connect.
- // The HSv4 clients do not interpret the error handshake response correctly.
- // HSv5: Send a handshake with an error code (hs.m_iReqType set earlier) to the peer.
- if (hs.m_iVersion < HS_VERSION_SRT1)
- {
- HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: HSv4 caller, sending SHUTDOWN after rejection with "
- << RequestTypeStr(hs.m_iReqType));
- CPacket rsp;
- setPacketTS((rsp), steady_clock::now());
- rsp.pack(UMSG_SHUTDOWN);
- rsp.m_iID = m_PeerID;
- m_pSndQueue->sendto(addr, rsp, use_source_addr);
- }
- else
- {
- HLOGC(cnlog.Debug,
- log << CONID() << "processConnectRequest: sending ABNORMAL handshake info req="
- << RequestTypeStr(hs.m_iReqType));
- size_t size = CHandShake::m_iContentSize;
- hs.store_to((packet.m_pcData), (size));
- packet.setLength(size);
- packet.m_iID = id;
- setPacketTS(packet, steady_clock::now());
- HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (a): " << hs.show());
- m_pSndQueue->sendto(addr, packet, use_source_addr);
- }
- }
- }
- LOGC(cnlog.Note, log << CONID() << "listen ret: " << hs.m_iReqType << " - " << RequestTypeStr(hs.m_iReqType));
- return RejectReasonForURQ(hs.m_iReqType);
- }
- void srt::CUDT::addLossRecord(std::vector<int32_t> &lr, int32_t lo, int32_t hi)
- {
- if (lo == hi)
- lr.push_back(lo);
- else
- {
- lr.push_back(lo | LOSSDATA_SEQNO_RANGE_FIRST);
- lr.push_back(hi);
- }
- }
- int srt::CUDT::checkACKTimer(const steady_clock::time_point &currtime)
- {
- int because_decision = BECAUSE_NO_REASON;
- if (currtime > m_tsNextACKTime.load() // ACK time has come
- // OR the number of sent packets since last ACK has reached
- // the congctl-defined value of ACK Interval
- // (note that none of the builtin congctls defines ACK Interval)
- || (m_CongCtl->ACKMaxPackets() > 0 && m_iPktCount >= m_CongCtl->ACKMaxPackets()))
- {
- // ACK timer expired or ACK interval is reached
- sendCtrl(UMSG_ACK);
- const steady_clock::duration ack_interval = m_CongCtl->ACKTimeout_us() > 0
- ? microseconds_from(m_CongCtl->ACKTimeout_us())
- : m_tdACKInterval;
- m_tsNextACKTime.store(currtime + ack_interval);
- m_iPktCount = 0;
- m_iLightACKCount = 1;
- because_decision = BECAUSE_ACK;
- }
- // Or the transfer rate is so high that the number of packets
- // have reached the value of SelfClockInterval * LightACKCount before
- // the time has come according to m_tsNextACKTime. In this case a "lite ACK"
- // is sent, which doesn't contain statistical data and nothing more
- // than just the ACK number. The "fat ACK" packets will be still sent
- // normally according to the timely rules.
- else if (m_iPktCount >= SELF_CLOCK_INTERVAL * m_iLightACKCount)
- {
- // send a "light" ACK
- sendCtrl(UMSG_ACK, NULL, NULL, SEND_LITE_ACK);
- ++m_iLightACKCount;
- because_decision = BECAUSE_LITEACK;
- }
- return because_decision;
- }
- int srt::CUDT::checkNAKTimer(const steady_clock::time_point& currtime)
- {
- // XXX The problem with working NAKREPORT with SRT_ARQ_ONREQ
- // is not that it would be inappropriate, but because it's not
- // implemented. The reason for it is that the structure of the
- // loss list container (m_pRcvLossList) is such that it is expected
- // that the loss records are ordered by sequence numbers (so
- // that two ranges sticking together are merged in place).
- // Unfortunately in case of SRT_ARQ_ONREQ losses must be recorded
- // as before, but they should not be reported, until confirmed
- // by the filter. By this reason they appear often out of order
- // and for adding them properly the loss list container wasn't
- // prepared. This then requires some more effort to implement.
- if (!m_config.bRcvNakReport || m_PktFilterRexmitLevel != SRT_ARQ_ALWAYS)
- return BECAUSE_NO_REASON;
- /*
- * m_config.bRcvNakReport enables NAK reports for SRT.
- * Retransmission based on timeout is bandwidth consuming,
- * not knowing what to retransmit when the only NAK sent by receiver is lost,
- * all packets past last ACK are retransmitted (rexmitMethod() == SRM_FASTREXMIT).
- */
- enterCS(m_RcvLossLock);
- const int loss_len = m_pRcvLossList->getLossLength();
- leaveCS(m_RcvLossLock);
- SRT_ASSERT(loss_len >= 0);
- int debug_decision = BECAUSE_NO_REASON;
- if (loss_len > 0)
- {
- if (currtime <= m_tsNextNAKTime.load())
- return BECAUSE_NO_REASON; // wait for next NAK time
- sendCtrl(UMSG_LOSSREPORT);
- debug_decision = BECAUSE_NAKREPORT;
- }
- m_tsNextNAKTime.store(currtime + m_tdNAKInterval);
- return debug_decision;
- }
- bool srt::CUDT::checkExpTimer(const steady_clock::time_point& currtime, int check_reason SRT_ATR_UNUSED)
- {
- // VERY HEAVY LOGGING
- #if ENABLE_HEAVY_LOGGING & 1
- static const char* const decisions [] = {
- "ACK",
- "LITE-ACK",
- "NAKREPORT"
- };
- string decision = "NOTHING";
- if (check_reason)
- {
- ostringstream decd;
- decision = "";
- for (int i = 0; i < LAST_BECAUSE_BIT; ++i)
- {
- int flag = 1 << i;
- if (check_reason & flag)
- decd << decisions[i] << " ";
- }
- decision = decd.str();
- }
- HLOGC(xtlog.Debug, log << CONID() << "checkTimer: ACTIVITIES PERFORMED: " << decision);
- #endif
- // In UDT the m_bUserDefinedRTO and m_iRTO were in CCC class.
- // There's nothing in the original code that alters these values.
- steady_clock::time_point next_exp_time;
- if (m_CongCtl->RTO())
- {
- next_exp_time = m_tsLastRspTime.load() + microseconds_from(m_CongCtl->RTO());
- }
- else
- {
- steady_clock::duration exp_timeout =
- microseconds_from(m_iEXPCount * (m_iSRTT + 4 * m_iRTTVar) + COMM_SYN_INTERVAL_US);
- if (exp_timeout < (m_iEXPCount * m_tdMinExpInterval))
- exp_timeout = m_iEXPCount * m_tdMinExpInterval;
- next_exp_time = m_tsLastRspTime.load() + exp_timeout;
- }
- if (currtime <= next_exp_time && !m_bBreakAsUnstable)
- return false;
- // ms -> us
- const int PEER_IDLE_TMO_US = m_config.iPeerIdleTimeout_ms * 1000;
- // Haven't received any information from the peer, is it dead?!
- // timeout: at least 16 expirations and must be greater than 5 seconds
- time_point last_rsp_time = m_tsLastRspTime.load();
- if (m_bBreakAsUnstable || ((m_iEXPCount > COMM_RESPONSE_MAX_EXP) &&
- (currtime - last_rsp_time > microseconds_from(PEER_IDLE_TMO_US))))
- {
- //
- // Connection is broken.
- // UDT does not signal any information about this instead of to stop quietly.
- // Application will detect this when it calls any UDT methods next time.
- //
- HLOGC(xtlog.Debug,
- log << CONID() << "CONNECTION EXPIRED after " << count_milliseconds(currtime - last_rsp_time) << "ms");
- m_bClosing = true;
- m_bBroken = true;
- m_iBrokenCounter = 30;
- // update snd U list to remove this socket
- m_pSndQueue->m_pSndUList->update(this, CSndUList::DO_RESCHEDULE);
- updateBrokenConnection();
- completeBrokenConnectionDependencies(SRT_ECONNLOST); // LOCKS!
- return true;
- }
- HLOGC(xtlog.Debug,
- log << CONID() << "EXP TIMER: count=" << m_iEXPCount << "/" << (+COMM_RESPONSE_MAX_EXP)
- << " elapsed=" << (count_microseconds(currtime - last_rsp_time)) << "/" << (+PEER_IDLE_TMO_US) << "us");
- ++m_iEXPCount;
- /*
- * (keepalive fix)
- * duB:
- * It seems there is confusion of the direction of the Response here.
- * lastRspTime is supposed to be when receiving (data/ctrl) from peer
- * as shown in processCtrl and processData,
- * Here we set because we sent something?
- *
- * Disabling this code that prevent quick reconnection when peer disappear
- */
- // Reset last response time since we've just sent a heart-beat.
- // (fixed) m_tsLastRspTime = currtime_tk;
- return false;
- }
- void srt::CUDT::checkRexmitTimer(const steady_clock::time_point& currtime)
- {
- // Check if HSv4 should be retransmitted, and if KM_REQ should be resent if the side is INITIATOR.
- checkSndTimers();
- // There are two algorithms of blind packet retransmission: LATEREXMIT and FASTREXMIT.
- //
- // LATEREXMIT is only used with FileCC.
- // The RTO is triggered when some time has passed since the last ACK from
- // the receiver, while there is still some unacknowledged data in the sender's buffer,
- // and the loss list is empty at the moment of RTO (nothing to retransmit yet).
- //
- // FASTREXMIT is only used with LiveCC.
- // The RTO is triggered if the receiver is not configured to send periodic NAK reports,
- // when some time has passed since the last ACK from the receiver,
- // while there is still some unacknowledged data in the sender's buffer.
- //
- // In case the above conditions are met, the unacknowledged packets
- // in the sender's buffer will be added to the SND loss list and retransmitted.
- //
- {
- ScopedLock ack_lock(m_RecvAckLock);
- const uint64_t rtt_syn = (m_iSRTT + 4 * m_iRTTVar + 2 * COMM_SYN_INTERVAL_US);
- const uint64_t exp_int_us = (m_iReXmitCount * rtt_syn + COMM_SYN_INTERVAL_US);
- if (currtime <= (m_tsLastRspAckTime + microseconds_from(exp_int_us)))
- return;
- }
- // If there is no unacknowledged data in the sending buffer,
- // then there is nothing to retransmit.
- if (m_pSndBuffer->getCurrBufSize() <= 0)
- return;
- const bool is_laterexmit = m_CongCtl->rexmitMethod() == SrtCongestion::SRM_LATEREXMIT; // FileCC
- const bool is_fastrexmit = m_CongCtl->rexmitMethod() == SrtCongestion::SRM_FASTREXMIT; // LiveCC
- // If the receiver will send periodic NAK reports, then FASTREXMIT (live) is inactive.
- // TODO: Probably some method of "blind rexmit" MUST BE DONE, when TLPKTDROP is off.
- if (is_fastrexmit && m_bPeerNakReport)
- return;
- // Schedule a retransmission IF:
- // - there are packets in flight (getFlightSpan() > 0);
- // - in case of LATEREXMIT (File Mode): the sender loss list is empty
- // (the receiver didn't send any LOSSREPORT, or LOSSREPORT was lost on track).
- // - in case of FASTREXMIT (Live Mode): the RTO (rtt_syn) was triggered, therefore
- // schedule unacknowledged packets for retransmission regardless of the loss list emptiness.
- if (getFlightSpan() > 0 && (!is_laterexmit || m_pSndLossList->getLossLength() == 0))
- {
- // Sender: Insert all the packets sent after last received acknowledgement into the sender loss list.
- ScopedLock acklock(m_RecvAckLock); // Protect packet retransmission
- // Resend all unacknowledged packets on timeout, but only if there is no packet in the loss list
- const int32_t csn = m_iSndCurrSeqNo;
- const int num = m_pSndLossList->insert(m_iSndLastAck, csn);
- if (num > 0)
- {
- enterCS(m_StatsLock);
- m_stats.sndr.lost.count(num);
- leaveCS(m_StatsLock);
- HLOGC(xtlog.Debug,
- log << CONID() << "ENFORCED " << (is_laterexmit ? "LATEREXMIT" : "FASTREXMIT")
- << " by ACK-TMOUT (scheduling): " << CSeqNo::incseq(m_iSndLastAck) << "-" << csn << " ("
- << CSeqNo::seqoff(m_iSndLastAck, csn) << " packets)");
- }
- }
- ++m_iReXmitCount;
- const ECheckTimerStage stage = is_fastrexmit ? TEV_CHT_FASTREXMIT : TEV_CHT_REXMIT;
- updateCC(TEV_CHECKTIMER, EventVariant(stage));
- // schedule sending if not scheduled already
- m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE);
- }
- void srt::CUDT::checkTimers()
- {
- // update CC parameters
- updateCC(TEV_CHECKTIMER, EventVariant(TEV_CHT_INIT));
- const steady_clock::time_point currtime = steady_clock::now();
- // This is a very heavy log, unblock only for temporary debugging!
- #if 0
- HLOGC(xtlog.Debug, log << CONID() << "checkTimers: nextacktime=" << FormatTime(m_tsNextACKTime)
- << " AckInterval=" << m_iACKInterval
- << " pkt-count=" << m_iPktCount << " liteack-count=" << m_iLightACKCount);
- #endif
- // Check if it is time to send ACK
- int debug_decision = checkACKTimer(currtime);
- // Check if it is time to send a loss report
- debug_decision |= checkNAKTimer(currtime);
- // Check if the connection is expired
- if (checkExpTimer(currtime, debug_decision))
- return;
- // Check if FAST or LATE packet retransmission is required
- checkRexmitTimer(currtime);
- if (currtime > m_tsLastSndTime.load() + microseconds_from(COMM_KEEPALIVE_PERIOD_US))
- {
- sendCtrl(UMSG_KEEPALIVE);
- #if ENABLE_BONDING
- if (m_parent->m_GroupOf)
- {
- ScopedLock glock (uglobal().m_GlobControlLock);
- if (m_parent->m_GroupOf)
- {
- // Pass socket ID because it's about changing group socket data
- m_parent->m_GroupOf->internalKeepalive(m_parent->m_GroupMemberData);
- // NOTE: GroupLock is unnecessary here because the only data read and
- // modified is the target of the iterator from m_GroupMemberData. The
- // iterator will be valid regardless of any container modifications.
- }
- }
- #endif
- HLOGP(xtlog.Debug, "KEEPALIVE");
- }
- }
- void srt::CUDT::updateBrokenConnection()
- {
- m_bClosing = true;
- releaseSynch();
- // app can call any UDT API to learn the connection_broken error
- uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR, true);
- CGlobEvent::triggerEvent();
- }
- void srt::CUDT::completeBrokenConnectionDependencies(int errorcode)
- {
- int token = -1;
- #if ENABLE_BONDING
- bool pending_broken = false;
- {
- ScopedLock guard_group_existence (uglobal().m_GlobControlLock);
- if (m_parent->m_GroupOf)
- {
- token = m_parent->m_GroupMemberData->token;
- if (m_parent->m_GroupMemberData->sndstate == SRT_GST_PENDING)
- {
- HLOGC(gmlog.Debug, log << CONID() << "updateBrokenConnection: a pending link was broken - will be removed");
- pending_broken = true;
- }
- else
- {
- HLOGC(gmlog.Debug,
- log << CONID() << "updateBrokenConnection: state="
- << CUDTGroup::StateStr(m_parent->m_GroupMemberData->sndstate)
- << " a used link was broken - not closing automatically");
- }
- m_parent->m_GroupMemberData->sndstate = SRT_GST_BROKEN;
- m_parent->m_GroupMemberData->rcvstate = SRT_GST_BROKEN;
- }
- }
- #endif
- if (m_cbConnectHook)
- {
- CALLBACK_CALL(m_cbConnectHook, m_SocketID, errorcode, m_PeerAddr.get(), token);
- }
- #if ENABLE_BONDING
- {
- // Lock GlobControlLock in order to make sure that
- // the state if the socket having the group and the
- // existence of the group will not be changed during
- // the operation. The attempt of group deletion will
- // have to wait until this operation completes.
- ScopedLock lock(uglobal().m_GlobControlLock);
- CUDTGroup* pg = m_parent->m_GroupOf;
- if (pg)
- {
- // Bound to one call because this requires locking
- pg->updateFailedLink();
- }
- }
- // Sockets that never succeeded to connect must be deleted
- // explicitly, otherwise they will never be deleted.
- if (pending_broken)
- {
- // XXX This somehow can cause a deadlock
- // uglobal()->close(m_parent);
- m_parent->setBrokenClosed();
- }
- #endif
- }
- void srt::CUDT::addEPoll(const int eid)
- {
- enterCS(uglobal().m_EPoll.m_EPollLock);
- m_sPollID.insert(eid);
- leaveCS(uglobal().m_EPoll.m_EPollLock);
- if (!stillConnected())
- return;
- enterCS(m_RecvLock);
- if (isRcvBufferReady())
- {
- uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, true);
- }
- leaveCS(m_RecvLock);
- if (m_config.iSndBufSize > m_pSndBuffer->getCurrBufSize())
- {
- uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, true);
- }
- }
- void srt::CUDT::removeEPollEvents(const int eid)
- {
- // clear IO events notifications;
- // since this happens after the epoll ID has been removed, they cannot be set again
- set<int> remove;
- remove.insert(eid);
- uglobal().m_EPoll.update_events(m_SocketID, remove, SRT_EPOLL_IN | SRT_EPOLL_OUT, false);
- }
- void srt::CUDT::removeEPollID(const int eid)
- {
- enterCS(uglobal().m_EPoll.m_EPollLock);
- m_sPollID.erase(eid);
- leaveCS(uglobal().m_EPoll.m_EPollLock);
- }
- void srt::CUDT::ConnectSignal(ETransmissionEvent evt, EventSlot sl)
- {
- if (evt >= TEV_E_SIZE)
- return; // sanity check
- m_Slots[evt].push_back(sl);
- }
- void srt::CUDT::DisconnectSignal(ETransmissionEvent evt)
- {
- if (evt >= TEV_E_SIZE)
- return; // sanity check
- m_Slots[evt].clear();
- }
- void srt::CUDT::EmitSignal(ETransmissionEvent tev, EventVariant var)
- {
- for (std::vector<EventSlot>::iterator i = m_Slots[tev].begin(); i != m_Slots[tev].end(); ++i)
- {
- i->emit(tev, var);
- }
- }
- int srt::CUDT::getsndbuffer(SRTSOCKET u, size_t *blocks, size_t *bytes)
- {
- CUDTSocket *s = uglobal().locateSocket(u);
- if (!s)
- return -1;
- CSndBuffer *b = s->core().m_pSndBuffer;
- if (!b)
- return -1;
- int bytecount, timespan;
- int count = b->getCurrBufSize((bytecount), (timespan));
- if (blocks)
- *blocks = count;
- if (bytes)
- *bytes = bytecount;
- return std::abs(timespan);
- }
- int srt::CUDT::rejectReason(SRTSOCKET u)
- {
- CUDTSocket* s = uglobal().locateSocket(u);
- if (!s)
- return SRT_REJ_UNKNOWN;
- return s->core().m_RejectReason;
- }
- int srt::CUDT::rejectReason(SRTSOCKET u, int value)
- {
- CUDTSocket* s = uglobal().locateSocket(u);
- if (!s)
- return APIError(MJ_NOTSUP, MN_SIDINVAL);
- if (value < SRT_REJC_PREDEFINED)
- return APIError(MJ_NOTSUP, MN_INVAL);
- s->core().m_RejectReason = value;
- return 0;
- }
- int64_t srt::CUDT::socketStartTime(SRTSOCKET u)
- {
- CUDTSocket* s = uglobal().locateSocket(u);
- if (!s)
- return APIError(MJ_NOTSUP, MN_SIDINVAL);
- return count_microseconds(s->core().m_stats.tsStartTime.time_since_epoch());
- }
- bool srt::CUDT::runAcceptHook(CUDT *acore, const sockaddr* peer, const CHandShake& hs, const CPacket& hspkt)
- {
- // Prepare the information for the hook.
- // We need streamid.
- char target[CSrtConfig::MAX_SID_LENGTH + 1];
- memset((target), 0, CSrtConfig::MAX_SID_LENGTH + 1);
- // Just for a case, check the length.
- // This wasn't done before, and we could risk memory crash.
- // In case of error, this will remain unset and the empty
- // string will be passed as streamid.
- int ext_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(hs.m_iType);
- #if ENABLE_BONDING
- bool have_group = false;
- SRT_GROUP_TYPE gt = SRT_GTYPE_UNDEFINED;
- #endif
- // This tests if there are any extensions.
- if (hspkt.getLength() > CHandShake::m_iContentSize + 4 && IsSet(ext_flags, CHandShake::HS_EXT_CONFIG))
- {
- uint32_t *begin = reinterpret_cast<uint32_t *>(hspkt.m_pcData + CHandShake::m_iContentSize);
- size_t size = hspkt.getLength() - CHandShake::m_iContentSize; // Due to previous cond check we grant it's >0
- uint32_t *next = 0;
- size_t length = size / sizeof(uint32_t);
- size_t blocklen = 0;
- for (;;) // ONE SHOT, but continuable loop
- {
- int cmd = FindExtensionBlock(begin, length, (blocklen), (next));
- const size_t bytelen = blocklen * sizeof(uint32_t);
- if (cmd == SRT_CMD_SID)
- {
- if (!bytelen || bytelen > CSrtConfig::MAX_SID_LENGTH)
- {
- LOGC(cnlog.Error,
- log << CONID() << "interpretSrtHandshake: STREAMID length " << bytelen << " is 0 or > "
- << +CSrtConfig::MAX_SID_LENGTH << " - PROTOCOL ERROR, REJECTING");
- return false;
- }
- // See comment at CUDT::interpretSrtHandshake().
- memcpy((target), begin + 1, bytelen);
- // Un-swap on big endian machines
- ItoHLA(((uint32_t *)target), (uint32_t *)target, blocklen);
- }
- #if ENABLE_BONDING
- else if (cmd == SRT_CMD_GROUP)
- {
- uint32_t* groupdata = begin + 1;
- have_group = true; // Even if parse error happes
- if (bytelen / sizeof(int32_t) >= GRPD_E_SIZE)
- {
- uint32_t gd = groupdata[GRPD_GROUPDATA];
- gt = SRT_GROUP_TYPE(SrtHSRequest::HS_GROUP_TYPE::unwrap(gd));
- }
- }
- #endif
- else if (cmd == SRT_CMD_NONE)
- {
- // End of blocks
- break;
- }
- // Any other kind of message extracted. Search on.
- if (!NextExtensionBlock((begin), next, (length)))
- break;
- }
- }
- #if ENABLE_BONDING
- if (have_group && acore->m_config.iGroupConnect == 0)
- {
- HLOGC(cnlog.Debug,
- log << CONID() << "runAcceptHook: REJECTING connection WITHOUT calling the hook - groups not allowed");
- return false;
- }
- // Update the groupconnect flag
- acore->m_config.iGroupConnect = have_group ? 1 : 0;
- acore->m_HSGroupType = gt;
- #endif
- // Set the default value
- acore->m_RejectReason = SRT_REJX_FALLBACK;
- try
- {
- int result = CALLBACK_CALL(m_cbAcceptHook, acore->m_SocketID, hs.m_iVersion, peer, target);
- if (result == -1)
- return false;
- }
- catch (...)
- {
- LOGP(cnlog.Warn, "runAcceptHook: hook interrupted by exception");
- return false;
- }
- acore->m_RejectReason = SRT_REJ_UNKNOWN;
- return true;
- }
- void srt::CUDT::processKeepalive(const CPacket& ctrlpkt, const time_point& tsArrival)
- {
- // Here can be handled some protocol definition
- // for extra data sent through keepalive.
- #if ENABLE_BONDING
- if (m_parent->m_GroupOf)
- {
- // Lock GlobControlLock in order to make sure that
- // the state if the socket having the group and the
- // existence of the group will not be changed during
- // the operation. The attempt of group deletion will
- // have to wait until this operation completes.
- ScopedLock lock(uglobal().m_GlobControlLock);
- CUDTGroup* pg = m_parent->m_GroupOf;
- if (pg)
- {
- // Whether anything is to be done with this socket
- // about the fact that keepalive arrived, let the
- // group handle it
- pg->processKeepalive(m_parent->m_GroupMemberData);
- }
- }
- #endif
- ScopedLock lck(m_RcvBufferLock);
- m_pRcvBuffer->updateTsbPdTimeBase(ctrlpkt.getMsgTimeStamp());
- if (m_config.bDriftTracer)
- m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), tsArrival, -1);
- }
|