123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806openCoremoduleSDN=Frenetic_kernel.OpenFlowmoduleField=struct(** The order of the constructors defines the default variable ordering and has a massive
performance impact. Do not change unless you know what you are doing. *)typet=Switch|Location|From|AbstractLoc|VSwitch|VPort|Vlan|VlanPcp(* SJS: for simplicity, support only up to 5 meta fields for now *)|Meta0|Meta1|Meta2|Meta3|Meta4|EthType|IPProto|EthSrc|EthDst|IP4Src|IP4Dst|TCPSrcPort|TCPDstPort|VFabric[@@derivingsexp,enumerate,enum,hash]typefield=tletnum_fields=max+1letof_strings=Sexp.of_strings|>t_of_sexpletto_stringt=sexp_of_tt|>Sexp.to_stringletis_valid_order(lst:tlist):bool=Set.Poly.(equal(of_listlst)(of_listall))letorder=Array.initnum_fields~f:identletset_order(lst:tlist):unit=assert(is_valid_orderlst);List.iterilst~f:(funifld->order.(to_enumfld)<-i)(* Not a clean way to invert a permutation, but fast *)letinvertarr=letinverted=Array.initnum_fields~f:identinArray.iteriarr~f:(funielt->inverted.(elt)<-i);invertedletget_order()=Array.to_list(invertorder)|>List.filter_map~f:of_enum(* compare depends on current order! *)letcompare(x:t)(y:t):int=(* using Obj.magic instead of to_enum for bettter performance *)Int.compareorder.(Obj.magicx)order.(Obj.magicy)letequalxy=Poly.(x=y)moduletypeENV=sigtypetvalempty:texceptionFullvaladd:t->string->Syntax.meta_init->bool->t(* may raise Full *)vallookup:t->string->field*(Syntax.meta_init*bool)(* may raise Not_found *)endmoduleEnv:ENV=structtypet={alist:(string*(field*(Syntax.meta_init*bool)))list;depth:int}letempty={alist=[];depth=0}exceptionFullletaddenvnameinitmut=letfield=matchenv.depthwith|0->Meta0|1->Meta1|2->Meta2|3->Meta3|4->Meta4|_->raiseFullin{alist=List.Assoc.add~equal:Poly.(=)env.alistname(field,(init,mut));depth=env.depth+1}letlookupenvname=List.Assoc.find_exn~equal:Poly.(=)env.alistnameendletof_hv?(env=Env.empty)hv=matchhvwith|Syntax.Switch_->Switch|Syntax.Location_->Location|Syntax.From_->From|Syntax.AbstractLoc_->AbstractLoc|Syntax.EthSrc_->EthSrc|Syntax.EthDst_->EthDst|Syntax.Vlan_->Vlan|Syntax.VlanPcp_->VlanPcp|Syntax.VSwitch_->VSwitch|Syntax.VPort_->VPort|Syntax.EthType_->EthType|Syntax.IPProto_->IPProto|Syntax.IP4Src_->IP4Src|Syntax.IP4Dst_->IP4Dst|Syntax.TCPSrcPort_->TCPSrcPort|Syntax.TCPDstPort_->TCPDstPort|Syntax.VFabric_->VFabric|Syntax.Meta(id,_)->fst(Env.lookupenvid)(* Heuristic to pick a variable order that operates by scoring the fields
in a policy. A field receives a high score if, when a test field=X
is false, the policy can be shrunk substantially.
NOTE(arjun): This could be done better, but it seems to work quite well
on FatTrees and the SDX benchmarks. Some ideas for improvement:
- Easy: also account for setting tests field=X suceeded
- Harder, but possibly much better: properly calculate the size of the
pol for different field assignments. Don't traverse the policy
repeatedly. Instead, write a size function that returns map from
field assignments to sizes. *)letauto_order(pol:Syntax.policy):unit=letopenSyntaxin(* Construct array of scores, where score starts at 0 for every field *)letcount_arr=Array.initnum_fields~f:(fun_->0)inletrecf_predsize(env,pred)=matchpredwith|True->()|False->()|Test(Syntax.Meta(id,_))->beginmatchEnv.lookupenvidwith|(f,(Aliashv,false))->letf=to_enumfinletf'=to_enum(of_hvhv)incount_arr.(f)<-count_arr.(f)+size;count_arr.(f')<-count_arr.(f')+size|(f,_)->letf=to_enumfincount_arr.(f)<-count_arr.(f)+sizeend|Testhv->letf=to_enum(of_hvhv)incount_arr.(f)<-count_arr.(f)+size|Or(a,b)->f_predsize(env,a);f_predsize(env,b)|And(a,b)->f_predsize(env,a);f_predsize(env,b)|Nega->f_predsize(env,a)inletrecf_seq'pollstenvk=matchpolwith|Mod_->k(1,lst)|Filtera->k(1,(env,a)::lst)|Seq(p,q)->f_seq'plstenv(fun(m,lst)->f_seq'qlstenv(fun(n,lst)->k(m*n,lst)))|Union_->k(f_unionpolenv,lst)|Let{id;init;mut;body=p}->letenv=Env.addenvidinitmutinf_seq'plstenvk|Starp->k(f_unionpenv,lst)|Link(sw,pt,_,_)->k(1,(env,Test(Switchsw))::(env,Test(Location(Physicalpt)))::lst)|VLink(sw,pt,_,_)->k(1,(env,Test(VSwitchsw))::(env,Test(VPortpt))::lst)|Dup->k(1,lst)andf_seqpolenv:int=let(size,preds)=f_seq'pol[]env(funx->x)inList.iterpreds~f:(f_predsize);sizeandf_union'pollstenvk=matchpolwith|Mod_->(1,lst)|Filtera->(1,(env,a)::lst)|Union(p,q)->f_union'plstenv(fun(m,lst)->f_union'qlstenv(fun(n,lst)->k(m+n,lst)))|Seq_->k(f_seqpolenv,lst)|Let{id;init;mut;body=p}->letenv=Env.addenvidinitmutink(f_seqpenv,lst)|Starp->f_union'plstenvk|Link(sw,pt,_,_)->k(1,(env,Test(Switchsw))::(env,Test(Location(Physicalpt)))::lst)|VLink(sw,pt,_,_)->k(1,(env,Test(VSwitchsw))::(env,Test(VPortpt))::lst)|Dup->k(1,lst)andf_unionpolenv:int=let(size,preds)=f_union'pol[]env(funx->x)inList.iterpreds~f:(f_predsize);sizeinlet_=f_seqpolEnv.emptyinArray.foldicount_arr~init:[]~f:(funiaccn->((Obj.magici,n)::acc))|>List.stable_sort~compare:(fun(_,x)(_,y)->Int.comparexy)|>List.rev(* SJS: do NOT remove & reverse order! Want stable sort *)|>List.map~f:fst|>set_orderendmoduleValue=structtypet=ConstofInt64.t|MaskofInt64.t*int|AbstractLocationofSyntax.abstract_location|Pipeofstring|Queryofstring(* TODO(grouptable): HACK, should only be able to fast fail on ports.
* Put this somewhere else *)|FastFailofInt32.tlist[@@derivingsexp,hash](* subseq_eq, meet and join are defined to make this fit interface of Vlr.Lattice *)letsubset_eqab=(* Note that Mask checking is a lot like OpenFlow.Pattern.Ip, but the int's are different sizes *)letsubset_eq_maskambn=ifPoly.(m<n)thenfalseelsePoly.(Int64.shift_right_logicala(64-n)=Int64.shift_right_logicalb(64-n))inmatcha,bwith|Consta,Constb(* Note that comparing a mask to a constant requires the mask to be all 64 bits, otherwise they fail the lesser mask test *)|Mask(a,64),Constb->Poly.(a=b)|AbstractLocationa,AbstractLocationb->Poly.(a=b)|Pipea,Pipeb|Querya,Queryb->Poly.(a=b)|Mask_,Const_|AbstractLocation_,_|Pipe_,_|Query_,_|FastFail_,_|_,AbstractLocation_|_,Pipe_|_,Query_|_,FastFail_->false|Mask(a,m),Mask(b,n)->subset_eq_maskambn|Consta,Mask(b,n)->subset_eq_maska64bnletmeet?(tight=false)ab=letmeet_maskambn=letlt=subset_eq(Mask(a,m))(Mask(b,n))inletgt=subset_eq(Mask(b,n))(Mask(a,m))iniflt&>thenSome(Mask(a,m))elseifltthenif(nottight)||(m=(n+1))thenSome(Mask(a,m))elseNoneelseifgtthenif(nottight)||(n=(m+1))thenSome(Mask(b,n))elseNoneelseNoneinmatcha,bwith|AbstractLocationa,AbstractLocationb->ifSyntax.equal_abstract_locationabthenSome(AbstractLocationa)elseNone|AbstractLocation_,_|_,AbstractLocation_->None|Consta,Constb|Mask(a,64),Constb->ifPoly.(a=b)thenSome(Consta)elseNone|Pipea,Pipeb->ifPoly.(a=b)thenSome(Pipea)elseNone|Querya,Queryb->ifPoly.(a=b)thenSome(Querya)elseNone|Mask_,Const_|Pipe_,_|Query_,_|_,Pipe_|_,Query_|FastFail_,_|_,FastFail_->None|Mask(a,m),Mask(b,n)->meet_maskambn|Consta,Mask(b,n)->meet_maska64bnletjoin?(tight=false)ab=(* The intent here looks a lot like OpenFlow.Pattern.Ip.join, but the notion of "tightness" might not
not apply. Look at perhaps sharing the logic between the two, abstracting out bit length since this deals with
64 bit ints *)letjoin_maskambn=letlt=subset_eq(Mask(a,m))(Mask(b,n))inletgt=subset_eq(Mask(b,n))(Mask(a,m))iniflt&>thenSome(Mask(a,m))elseifltthenif(nottight)||(n=(m-1))thenSome(Mask(b,n))elseNoneelseifgtthenif(nottight)||(m=(n-1))thenSome(Mask(a,m))elseNoneelseif(nottight)||m=nthenletx,y=(Mask(a,m-1),Mask(b,n-1))inifsubset_eqxy&&subset_eqyxthenSome(x)elseNoneelseNone(* XXX(seliopou): complete definition *)inmatcha,bwith|AbstractLocationa,AbstractLocationb->ifSyntax.equal_abstract_locationabthenSome(AbstractLocationa)elseNone|AbstractLocation_,_|_,AbstractLocation_->None|Consta,Constb|Mask(a,64),Constb->ifPoly.(a=b)thenSome(Consta)elseNone|Pipea,Pipeb->ifPoly.(a=b)thenSome(Pipea)elseNone|Querya,Queryb->ifPoly.(a=b)thenSome(Querya)elseNone|Mask_,Const_|Pipe_,_|Query_,_|_,Pipe_|_,Query_|FastFail_,_|_,FastFail_->None|Mask(a,m),Mask(b,n)->join_maskambn|Consta,Mask(b,n)->join_maska64bn(* Value compare is used in Pattern below, but is not public *)letcomparexy=match(x,y)with|Consta,Mask(b,64)|Mask(a,64),Constb|Consta,Constb->Int64.compareab|Querys1,Querys2|Pipes1,Pipes2->String.compares1s2|FastFaill1,FastFaill2->List.compareInt32.comparel1l2|Mask(a,m),Mask(b,n)->letshift=64-minmnin(matchInt64.(compare(shift_rightashift)(shift_rightbshift))with|0->Int.comparenm|c->c)|AbstractLocationa,AbstractLocationb->Syntax.compare_abstract_locationab|Const_,_->-1|_,Const_->1|Mask_,_->-1|_,Mask_->1|AbstractLocation_,_->-1|_,AbstractLocation_->1|Query_,_->-1|_,Query_->1|Pipe_,_->-1|_,Pipe_->1letequalxy=comparexy=0letto_string=function|Const(a)->Printf.sprintf"%Lu"a|Mask(a,m)->Printf.sprintf"%Lu/%d"am|AbstractLocation(s)->Printf.sprintf"%s"s|Pipe(p)->Printf.sprintf"Pipe(%s)"p|Query(p)->Printf.sprintf"Query(%s)"p|FastFail(p_lst)->Printf.sprintf"FastFail(%s)"(Syntax.string_of_fastfailp_lst)letof_intt=Const(Int64.of_intt)(* Private to this file only *)letof_int32t=Const(Int64.of_int32t)letof_int64t=Consttletto_int64_exn=function|Constk->k|_->assertfalseendexceptionFieldValue_mismatchofField.t*Value.tmodulePattern=structtypet=Field.t*Value.t[@@derivingcompare]letto_string(f,v)=Printf.sprintf"%s = %s"(Field.to_stringf)(Value.to_stringv)letequalab=compareab=0letto_int=Int64.to_int_exnletto_int32=Int64.to_int32_exnletof_hv?(env=Field.Env.empty)hv=letopenSyntaxinmatchhvwith|Switchsw_id->(Field.Switch,Value.(Constsw_id))|Location(Physicalp)->(Field.Location,Value.of_int32p)|Fromloc->(Field.From,Value.AbstractLocationloc)|AbstractLocloc->(Field.AbstractLoc,Value.AbstractLocationloc)(* TODO(grouptable): value hack *)|Location(FastFailp_lst)->(Field.Location,Value.(FastFailp_lst))|Location(Pipep)->(Field.Location,Value.(Pipep))|Location(Queryp)->(Field.Location,Value.(Queryp))|EthSrc(dlAddr)->(Field.EthSrc,Value.(ConstdlAddr))|EthDst(dlAddr)->(Field.EthDst,Value.(ConstdlAddr))|Vlan(vlan)->(Field.Vlan,Value.of_intvlan)|VlanPcp(vlanPcp)->(Field.VlanPcp,Value.of_intvlanPcp)|VSwitch(vsw_id)->(Field.VSwitch,Value.(Constvsw_id))|VPort(vpt)->(Field.VPort,Value.(Constvpt))|EthType(dlTyp)->(Field.EthType,Value.of_intdlTyp)|IPProto(nwProto)->(Field.IPProto,Value.of_intnwProto)|IP4Src(nwAddr,mask)->(Field.IP4Src,Value.(Mask(Int64.of_int32nwAddr,32+(Int32.to_int_exnmask))))|IP4Dst(nwAddr,mask)->(Field.IP4Dst,Value.(Mask(Int64.of_int32nwAddr,32+(Int32.to_int_exnmask))))|TCPSrcPort(tpPort)->(Field.TCPSrcPort,Value.of_inttpPort)|TCPDstPort(tpPort)->(Field.TCPDstPort,Value.of_inttpPort)|VFabric(vfab)->(Field.VFabric,Value.(Constvfab))|Meta(name,v)->(fst(Field.Env.lookupenvname),Value.(Constv))letto_hv(f,v)=letopenFieldinletopenValueinmatchf,vwith|(Switch,Constsw)->Syntax.Switchsw|(Location,Constp)->Syntax.(Location(Physical(to_int32p)))|(Location,Pipep)->Syntax.(Location(Pipep))|(Location,Queryq)->Syntax.(Location(Queryq))|(From,AbstractLocationl)->Syntax.Froml|(AbstractLoc,AbstractLocationl)->Syntax.AbstractLocl|(EthSrc,ConstdlAddr)->Syntax.(EthSrcdlAddr)|(EthDst,ConstdlAddr)->Syntax.(EthDstdlAddr)|(Vlan,Constvlan)->Syntax.(Vlan(to_intvlan))|(VlanPcp,ConstvlanPcp)->Syntax.(VlanPcp(to_intvlanPcp))|(VSwitch,Constvsw)->Syntax.VSwitchvsw|(VPort,Constvpt)->Syntax.VPortvpt|(EthType,ConstdlTyp)->Syntax.(EthType(to_intdlTyp))|(IPProto,ConstnwProto)->Syntax.(IPProto(to_intnwProto))|(IP4Src,Mask(nwAddr,mask))->Syntax.(IP4Src(to_int32nwAddr,Int32.of_int_exn(mask-32)))|(IP4Src,ConstnwAddr)->Syntax.(IP4Src(to_int32nwAddr,32l))|(IP4Dst,Mask(nwAddr,mask))->Syntax.(IP4Dst(to_int32nwAddr,Int32.of_int_exn(mask-32)))|(IP4Dst,ConstnwAddr)->Syntax.(IP4Dst(to_int32nwAddr,32l))|(TCPSrcPort,ConsttpPort)->Syntax.(TCPSrcPort(to_inttpPort))|(TCPDstPort,ConsttpPort)->Syntax.(TCPDstPort(to_inttpPort))|(VFabric,Constvfab)->Syntax.VFabricvfab|_,_->raise(FieldValue_mismatch(f,v))letto_pred(f,v)=Syntax.Test(to_hv(f,v))letto_sdn(f,v):SDN.Pattern.t->SDN.Pattern.t=letopenFieldinletopenValueinmatchf,vwith|(Location,Constp)->funpat->{patwithSDN.Pattern.inPort=Some(to_int32p)}|(EthSrc,ConstdlAddr)->funpat->{patwithSDN.Pattern.dlSrc=Some(dlAddr)}|(EthDst,ConstdlAddr)->funpat->{patwithSDN.Pattern.dlDst=Some(dlAddr)}|(Vlan,Constvlan)->funpat->{patwithSDN.Pattern.dlVlan=Some(to_intvlan)}|(VlanPcp,ConstvlanPcp)->funpat->{patwithSDN.Pattern.dlVlanPcp=Some(to_intvlanPcp)}|(EthType,ConstdlTyp)->funpat->{patwithSDN.Pattern.dlTyp=Some(to_intdlTyp)}|(IPProto,ConstnwProto)->funpat->{patwithSDN.Pattern.nwProto=Some(to_intnwProto)}|(IP4Src,Mask(nwAddr,mask))->funpat->{patwithSDN.Pattern.nwSrc=Some(to_int32nwAddr,Int32.of_int_exn(mask-32))}|(IP4Src,ConstnwAddr)->funpat->{patwithSDN.Pattern.nwSrc=Some(to_int32nwAddr,32l)}|(IP4Dst,Mask(nwAddr,mask))->funpat->{patwithSDN.Pattern.nwDst=Some(to_int32nwAddr,Int32.of_int_exn(mask-32))}|(IP4Dst,ConstnwAddr)->funpat->{patwithSDN.Pattern.nwDst=Some(to_int32nwAddr,32l)}|(TCPSrcPort,ConsttpPort)->funpat->{patwithSDN.Pattern.tpSrc=Some(to_inttpPort)}|(TCPDstPort,ConsttpPort)->funpat->{patwithSDN.Pattern.tpDst=Some(to_inttpPort)}(* Should never happen because these pseudo-fields should have been removed by the time to_sdn is used *)|(Switch,Const_)|(From,AbstractLocation_)|(AbstractLoc,AbstractLocation_)|(VSwitch,Const_)|(VPort,Const_)|(VFabric,Const_)|(Meta0,Const_)|(Meta1,Const_)|(Meta2,Const_)|(Meta3,Const_)|(Meta4,Const_)->assertfalse|_,_->raise(FieldValue_mismatch(f,v))endmoduleAction=structmoduleField_or_cont=structtypet=|FofField.t|K[@@derivingsexp,compare,hash,eq]endtypefield_or_cont=Field_or_cont.t=|FofField.t|K[@@derivingsexp,compare,hash,eq]moduleSeq=structmoduleM=Map.Make(Field_or_cont)includeM(* let equal = equal Value.equal *)letcompare=compare_directValue.comparelethash_fold_t(f:'vHash.folder)(s:Hash.state)(t:'vt)=Map.hash_fold_directField_or_cont.hash_fold_tfstletfold_fieldsseq~init~f=foldseq~init~f:(fun~key~dataacc->matchkeywith|Fkey->f~key~dataacc|_->acc)letprods1s2=(* Favor modifications to the right *)merges1s2~f:(fun~keym->matchmwith|`Both(_,v)|`Leftv|`Rightv->Some(v))letequal_mod_ks1s2=equalValue.equal(removes1K)(removes2K)letcompare_mod_ks1s2=compare(removes1K)(removes2K)letto_hvsseq=seq|>to_alist|>List.filter_map~f:(function(Ff,v)->Some(f,v)|_->None)letto_string(t:Value.tt):string=lets=to_alistt|>List.map~f:(fun(f,v)->letf=matchfwith|K->"state"|Ff->Field.to_stringfinsprintf"%s := %s"f(Value.to_stringv))|>String.concat~sep:", "in"["^s^"]"endmodulePar=structincludeSet.Make(structtypet=Value.tSeq.t[@@derivingsexp]letcompare=Seq.compareend)lethash_fold_t(s:Hash.state)(t:t)=Set.hash_fold_direct(Seq.hash_fold_tValue.hash_fold_t)stlethash(t:t)=hash_fold_t(Hash.alloc())t|>Hash.get_hash_valueletto_hvspar=foldpar~init:[]~f:(funaccseq->Seq.to_hvsseq@acc)letto_stringt:string=lets=to_listt|>List.map~f:Seq.to_string|>String.concat~sep:"; "in"{"^s^"}"letmod_k=map~f:(funseq->Seq.removeseqK)letcompare_mod_kp1p2=compare(mod_kp1)(mod_kp2)letequal_mod_kp1p2=equal(mod_kp1)(mod_kp2)endincludeParletone=Par.singletonSeq.emptyletzero=Par.emptyletis_one=Par.equaloneletis_zero=Par.is_emptyletsum(a:t)(b:t):t=(* This implements parallel composition specifically for NetKAT
modifications. *)ifis_zeroathenb(* 0 + p = p *)elseifis_zerobthena(* p + 0 = p *)elsePar.unionabletprod(a:t)(b:t):t=(* This implements sequential composition specifically for NetKAT
modifications and makes use of NetKAT laws to simplify results.*)ifis_zeroathenzero(* 0; p == 0 *)elseifis_zerobthenzero(* p; 0 == 0 *)elseifis_oneathenb(* 1; p == p *)elseifis_onebthena(* p; 1 == p *)elsePar.folda~init:zero~f:(funaccseq1->(* cannot implement sequential composition of this kind here *)let_=assert(matchSeq.findseq1KwithNone->true|_->false)inletr=Par.mapb~f:(Seq.prodseq1)inPar.unionaccr)letnegatet:t=(* This implements negation for the [zero] and [one] actions. Any
non-[zero] action will be mapped to [zero] by this function. *)ifis_zerotthenoneelsezeroletget_queries(t:t):stringlist=Par.foldt~init:[]~f:(funqueriesseq->matchSeq.findseq(FLocation)with|Some(Querystr)->str::queries|_->queries)letto_sdn?group_tbl(in_port:int64option)(t:t):SDN.par=letto_int=Int64.to_int_exninletto_int32=Int64.to_int32_exninlett=Par.filter_mapt~f:(funseq->(* Queries are equivalent to drop, so remove any [Seq.t]'s from the
* [Par.t] that set the location to a query.
*
* Pipe locations are no longer relevant to compilation, so rewrite all
* all of them to the empty string. This will allow multiple singleton
* [Seq.t]'s of port location assignments in a [Par.t] to be collapsed
* into into one. *)matchSeq.findseq(FField.Location)with|Some(Value.Query_)->None|Some(Value.Pipe_)->Some(Seq.setseq(FField.Location)(Value.Pipe""))|_->Some(seq))inletto_portp=matchin_portwith|Some(p')whenPoly.(p=p')->SDN.InPort|_->SDN.(Physical(to_int32p))inPar.foldt~init:[]~f:(funaccseq->letopenFieldinletopenValueinletinit=matchSeq.findseq(FLocation)with|None->[SDN.(Output(InPort))]|Some(Constp)->[SDN.(Output(to_portp))]|Some(Pipe_)->[SDN.(Output(Controller128))]|Some(Query_)->assertfalse|Some(FastFailp_lst)->(matchgroup_tblwith|Sometbl->letgid=Frenetic_kernel.GroupTable0x04.add_fastfail_grouptblp_lstin[SDN.(FastFailgid)]|None->failwith"fast failover present, but no group table provided!")|Somemask->raise(FieldValue_mismatch(Location,mask))inSeq.fold(Seq.removeseq(FLocation))~init~f:(fun~key~dataacc->matchkey,datawith|FFrom,AbstractLocationloc->raiseSyntax.Non_local|FAbstractLoc,AbstractLocationloc->raiseSyntax.Non_local|FSwitch,Constswitch->raiseSyntax.Non_local|FSwitch,_->raise(FieldValue_mismatch(Switch,data))|FLocation,_->assertfalse|FEthSrc,ConstdlAddr->SDN.(Modify(SetEthSrcdlAddr))::acc|FEthDst,ConstdlAddr->SDN.(Modify(SetEthDstdlAddr))::acc|FVlan,Constvlan->SDN.(Modify(SetVlan(Some(to_intvlan))))::acc|FVlanPcp,ConstvlanPcp->SDN.(Modify(SetVlanPcp(to_intvlanPcp)))::acc|FVSwitch,Const_|FVPort,Const_|FVFabric,Const_->assertfalse(* JNF: danger, danger *)|FEthType,ConstdlTyp->SDN.(Modify(SetEthTyp(to_intdlTyp)))::acc|FIPProto,ConstnwProto->SDN.(Modify(SetIPProto(to_intnwProto)))::acc|FIP4Src,Mask(nwAddr,64)|FIP4Src,ConstnwAddr->SDN.(Modify(SetIP4Src(to_int32nwAddr)))::acc|FIP4Dst,Mask(nwAddr,64)|FIP4Dst,ConstnwAddr->SDN.(Modify(SetIP4Dst(to_int32nwAddr)))::acc|FTCPSrcPort,ConsttpPort->SDN.(Modify(SetTCPSrcPort(to_inttpPort)))::acc|FTCPDstPort,ConsttpPort->SDN.(Modify(SetTCPDstPort(to_inttpPort)))::acc|Ff,_->raise(FieldValue_mismatch(f,data))|K,_->assertfalse)::acc)letdemod(f,v)t=Par.foldt~init:zero~f:(funaccseq->letseq'=matchSeq.findseq(Ff)with|Some(v')whenValue.comparevv'=0->Seq.removeseq(Ff)|_->seqinsumacc(Par.singletonseq'))letto_policyt=letopenSyntaxinPar.foldt~init:drop~f:(funaccseq->letseq'=Seq.fold_fieldsseq~init:id~f:(fun~key~dataacc->lethv=matchPattern.to_hv(key,data)with|IP4Src(nwAddr,32l)->IP4Src(nwAddr,32l)|IP4Dst(nwAddr,32l)->IP4Dst(nwAddr,32l)|IP4Src_|IP4Dst_->raise(FieldValue_mismatch(key,data))|hv->hvinOptimize.mk_seq(Mod(hv))acc)inOptimize.mk_unionseq'acc)letfold_fvt~(init:'a)~(f:'a->field:Field.t->value:Value.t->'a):'a=Par.foldt~init~f:(funaccseq->Seq.foldseq~init:acc~f:(fun~key~dataacc->matchkeywith|Fkey->facc~field:key~value:data|_->acc))letpipest=fold_fvt~init:String.Set.empty~f:(funacc~field~value->matchfield,valuewith|Field.Location,Value.Pipeq->Set.addaccq|_,_->acc)letqueriest=fold_fvt~init:String.Set.empty~f:(funacc~field~value->matchfield,valuewith|Field.Location,Value.Queryq->Set.addaccq|_,_->acc)|>Set.to_listletcompare=Par.compareletsize=Par.fold~init:0~f:(funaccseq->acc+(Seq.lengthseq))letto_string=Par.to_string(* let par = to_sdn ~group_tbl:(Frenetic_kernel.GroupTable0x04.create ()) None t in
Printf.sprintf "[%s]" (SDN.string_of_par par) *)endmoduleFDD=structincludeVlr.Make(Field)(Value)(Action)letmk_contk=constAction.(Par.singleton(Seq.singletonK(Value.of_int64k)))letcontsfdd=foldfdd~f:(funpar->Action.Par.foldpar~init:Int64.Set.empty~f:(funaccseq->matchAction.(Seq.findseqK)with|None->acc|Somek->Value.to_int64_exnk|>Int64.Set.addacc))~g:(fun_tf->Set.uniontf)letmap_contsfdd~(f:int64->int64)=letopenActioninletfpar=Par.mappar~f:(funseq->Seq.changeseqK(function|None->failwith"continuation expected, but none found"|Somek->Some(k|>Value.to_int64_exn|>f|>Value.of_int64)))inmap_rffddletequivalentt1t2=(* A context represents the set of packets that can reach a certain node.
It is implemented simply as a partial map from fields to values.
*)letmoduleCtxt=Action.Seqinletrecdo_nodest1t2ctxt=matchungett1,ungett2with|Branch{test=(f1,v1);tru=l1;fls=r1;all_fls=all_fls_1},Branch{test=(f2,v2);tru=l2;fls=r2;all_fls=all_fls_2}->beginmatchField.comparef1f2with|-1->do_nodesl1t2Ctxt.(setctxt(Ff1)v1)&&do_nodesr1t2ctxt|1->do_nodest1l2Ctxt.(setctxt(Ff2)v2)&&do_nodest1r2ctxt|0->beginmatchValue.comparev1v2with|0->do_nodesl1l2Ctxt.(setctxt(Ff1)v1)&&do_nodesr1r2ctxt|-1->do_nodesl1all_fls_2Ctxt.(setctxt(Ff1)v1)&&do_nodesr1t2ctxt|1->do_nodesall_fls_1l2Ctxt.(setctxt(Ff2)v2)&&do_nodest1r2ctxt|_->assertfalseend|_->assertfalseend|Branch{test=(f1,v1);tru=l1;fls=r1},Leaf_->do_nodesl1t2Ctxt.(setctxt(Ff1)v1)&&do_nodesr1t2ctxt|Leaf_,Branch{test=(f2,v2);tru=l2;fls=r2}->do_nodest1l2Ctxt.(setctxt(Ff2)v2)&&do_nodest1r2ctxt|Leafpar1,Leafpar2->Action.Par.equal(normalizepar1ctxt)(normalizepar2ctxt)andnormalizeparctxt=(* actions are context dependent; here we canonicalize them by interpreting
them in the context.
*)Action.Par.mappar~f:(Action.Seq.prodctxt)indo_nodest1t2Ctxt.emptyend