123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874(* (c) 2017, 2018 Hannes Mehnert, all rights reserved *)(* please read this before changing this module:
Data encoded by this module is persisted in (a) dump file and
(b) certificates (and subCA). It is important to be aware of backward and
forward compatibility when modifying this module. There are various version
fields around which are mostly useless in retrospect. On a server deployment,
upgrades are supported while downgrades are not (there could be a separate
tool reading newer data and dumping it for older albatross versions). The
assumption is that a server deployment moves forward. For the clients, older
clients should best be support smoothly, or an error from the server should
be issued informing about a too old version. Clients which support newer
wire version should as well be notified (it may be suitable to have a
--use-version command-line flag - so new clients can talk to old servers).
It should be ensured that old unikernels dumped to disk (a) can be read by
new albatross daemons. The functions state_to_str and
state_of_str are used for dump and restore, each an explicit choice.
They use the trie of unikernel_config, dump always uses the latest version in
the explicit choice. There's no version field involved.
The data in transit (certificates and commands) is out of control of a single
operator. This means that best effort should be done to support old clients
(and old servers - eventually with a command-line argument --use-version). If
a server receives a command (via TLS cert_extension), this is prefixed by a
version. The non-TLS command is a sequence of header and payload, where the
header includes a version. At the moment, the commands are all explicit
choices, adding new ones by extending the choice works in a
backwards-compatible way.
*)(* The version field could be used (at the moment, decoding a newer version
leads to a decoding failure):
Now, to achieve version-dependent parsing, what is missing is a way to decode
the first element of a sequence only (i.e. treat the second element as
"any"). This is something missing for PKCS12 from the asn1 library. A
"quick hack" is to extract length information of the first element, and use
that decoder on the sub-buffer. The following implements this. *)letguardperr=ifpthenOk()elseErrorerrlet(let*)=Result.bindletversion=letfdata=matchdatawith|5->`AV5|4->`AV4|3->`AV3|x->Asn.S.parse_error"unknown version number 0x%X"xandg=function|`AV5->5|`AV4->4|`AV3->3inAsn.S.mapfgAsn.S.intletdecode_seq_lenbuf=(* we assume a ASN.1 DER/BER encoded sequence starting in buf:
- 0x30
- length (definite length field - not 0x80)
- <data> (of length length)
*)let*()=guard(String.lengthbuf>2)(`Msg"buffer too short")inlet*()=guard(String.get_uint8buf0=0x30)(`Msg"not a sequence")inletl1=String.get_uint8buf1inlet*(off,l)=ifl1<0x80thenOk(2,l1)elseifl1=0x80thenError(`Msg"indefinite length")elseletoctets=l1land0x7Finlet*()=guard(String.lengthbuf-2>octets)(`Msg"data too short")inletrecgooffacc=ifoff=octetsthenOk(2+octets,acc)elsego(succoff)(String.get_uint8buf(off+2)+acclsl8)ingo00inlet*()=guard(String.lengthbuf-off>=l)(`Msg"buffer too small")inOk(off,l)letseq_hdbuf=let*(off,l)=decode_seq_lenbufinOk(String.subbufoffl)letdecode_wire_versionbuf=let*buf=seq_hdbufin(* from wire, sequence2 (header, payload) *)let*buf=seq_hdbufin(* from header, sequence3 (version ,__) *)letc=Asn.codecAsn.derversioninmatchAsn.decodecbufwith|Ok(a,_)->Oka|Error(`Parsemsg)->Error(`Msgmsg)openVmm_coreopenVmm_commandsletoid=Asn.OID.(base13<|6<|1<|4<|1<|49836<|42)letdecode_strictcodecbuf=matchAsn.decodecodecbufwith|Ok(a,rest)->let*()=guard(String.lengthrest=0)(`Msg"trailing bytes")inOka|Error(`Parsemsg)->Error(`Msgmsg)letprojections_ofasn=letc=Asn.codecAsn.derasnin(decode_strictc,Asn.encodec)letpolicy=letf(cpuids,unikernels,memory,block,bridges)=letbridges=String_set.of_listbridgesandcpuids=IS.of_listcpuidsinPolicy.{unikernels;cpuids;memory;block;bridges}andgpolicy=(IS.elementspolicy.Policy.cpuids,policy.Policy.unikernels,policy.Policy.memory,policy.Policy.block,String_set.elementspolicy.Policy.bridges)inAsn.S.mapfg@@Asn.S.(sequence5(required~label:"cpuids"Asn.S.(sequence_ofint))(required~label:"unikernels"int)(required~label:"memory"int)(optional~label:"block"int)(required~label:"bridges"Asn.S.(sequence_ofutf8_string)))letmy_explicit:?cls:Asn.S.cls->int->?label:string->'aAsn.S.t->'aAsn.S.t=fun?clsid?label:_asn->Asn.S.explicit?clsidasnletconsole_cmd=letf=function|`C1()->`Console_add|`C2`C1ts->`Old_console_subscribe(`Sincets)|`C2`C2c->`Old_console_subscribe(`Countc)|`C3`C1ts->`Console_subscribe(`Sincets)|`C3`C2c->`Console_subscribe(`Countc)andg=function|`Console_add->`C1()|`Old_console_subscribe`Sincets->`C2(`C1ts)|`Old_console_subscribe`Countc->`C2(`C2c)|`Console_subscribe`Sincets->`C3(`C1ts)|`Console_subscribe`Countc->`C3(`C2c)inAsn.S.mapfg@@Asn.S.(choice3(my_explicit0~label:"add"null)(my_explicit1~label:"old-subscribe"(choice2(my_explicit0~label:"since"utc_time)(my_explicit1~label:"count"int)))(my_explicit2~label:"subscribe"(choice2(my_explicit0~label:"since"generalized_time)(my_explicit1~label:"count"int))))(* TODO is this good? *)letint64=letfbuf=String.get_int64_bebuf0andgdata=letbuf=Bytes.create8inBytes.set_int64_bebuf0data;Bytes.unsafe_to_stringbufinAsn.S.mapfgAsn.S.octet_stringletmac_addr=letfcs=Result.fold(Macaddr.of_octetscs)~ok:Fun.id~error:(function`Msge->Asn.S.parse_error"bad mac address: %s"e)andgmac=Macaddr.to_octetsmacinAsn.S.mapfgAsn.S.octet_stringlettimeval=Asn.S.(sequence2(required~label:"seconds"int64)(required~label:"microseconds"int))letru=letopenStatsinletf(utime,(stime,(maxrss,(ixrss,(idrss,(isrss,(minflt,(majflt,(nswap,(inblock,(outblock,(msgsnd,(msgrcv,(nsignals,(nvcsw,nivcsw)))))))))))))))={utime;stime;maxrss;ixrss;idrss;isrss;minflt;majflt;nswap;inblock;outblock;msgsnd;msgrcv;nsignals;nvcsw;nivcsw}andgru=(ru.utime,(ru.stime,(ru.maxrss,(ru.ixrss,(ru.idrss,(ru.isrss,(ru.minflt,(ru.majflt,(ru.nswap,(ru.inblock,(ru.outblock,(ru.msgsnd,(ru.msgrcv,(ru.nsignals,(ru.nvcsw,ru.nivcsw)))))))))))))))inAsn.S.mapfg@@Asn.S.(sequence@@(required~label:"utime"timeval)@(required~label:"stime"timeval)@(required~label:"maxrss"int64)@(required~label:"ixrss"int64)@(required~label:"idrss"int64)@(required~label:"isrss"int64)@(required~label:"minflt"int64)@(required~label:"majflt"int64)@(required~label:"nswap"int64)@(required~label:"inblock"int64)@(required~label:"outblock"int64)@(required~label:"msgsnd"int64)@(required~label:"msgrcv"int64)@(required~label:"nsignals"int64)@(required~label:"nvcsw"int64)-@(required~label:"nivcsw"int64))(* although this changed (+runtime + cow + start) from V3 to V4, since it's not
persistent, no need to care about it *)letkinfo_mem=letopenStatsinletf(vsize,(rss,(tsize,(dsize,(ssize,(runtime,(cow,start)))))))={vsize;rss;tsize;dsize;ssize;runtime;cow;start}andgt=(t.vsize,(t.rss,(t.tsize,(t.dsize,(t.ssize,(t.runtime,(t.cow,t.start)))))))inAsn.S.mapfg@@Asn.S.(sequence@@(required~label:"bsize"int64)@(required~label:"rss"int64)@(required~label:"tsize"int64)@(required~label:"dsize"int64)@(required~label:"ssize"int64)@(required~label:"runtime"int64)@(required~label:"cow"int)-@(required~label:"start"timeval))(* TODO is this good? *)letint32=letfi=Int32.of_intiandgi=Int32.to_intiinAsn.S.mapfgAsn.S.intletifdata=letopenStatsinletf(bridge,(flags,(send_length,(max_send_length,(send_drops,(mtu,(baudrate,(input_packets,(input_errors,(output_packets,(output_errors,(collisions,(input_bytes,(output_bytes,(input_mcast,(output_mcast,(input_dropped,output_dropped)))))))))))))))))={bridge;flags;send_length;max_send_length;send_drops;mtu;baudrate;input_packets;input_errors;output_packets;output_errors;collisions;input_bytes;output_bytes;input_mcast;output_mcast;input_dropped;output_dropped}andgi=(i.bridge,(i.flags,(i.send_length,(i.max_send_length,(i.send_drops,(i.mtu,(i.baudrate,(i.input_packets,(i.input_errors,(i.output_packets,(i.output_errors,(i.collisions,(i.input_bytes,(i.output_bytes,(i.input_mcast,(i.output_mcast,(i.input_dropped,i.output_dropped)))))))))))))))))inAsn.S.mapfg@@Asn.S.(sequence@@(required~label:"bridge"utf8_string)@(required~label:"flags"int32)@(required~label:"send-length"int32)@(required~label:"max-send-length"int32)@(required~label:"send-drops"int32)@(required~label:"mtu"int32)@(required~label:"baudrate"int64)@(required~label:"input-packets"int64)@(required~label:"input-errors"int64)@(required~label:"output-packets"int64)@(required~label:"output-errors"int64)@(required~label:"collisions"int64)@(required~label:"input-bytes"int64)@(required~label:"output-bytes"int64)@(required~label:"input-mcast"int64)@(required~label:"output-mcast"int64)@(required~label:"input-dropped"int64)-@(required~label:"output-dropped"int64))letstats_cmd=letf=function|`C1(name,pid,taps)->`Stats_add(name,pid,taps)|`C2()->`Stats_remove|`C3()->`Stats_subscribe|`C4()->`Stats_initialandg=function|`Stats_add(name,pid,taps)->`C1(name,pid,taps)|`Stats_remove->`C2()|`Stats_subscribe->`C3()|`Stats_initial->`C4()inAsn.S.mapfg@@Asn.S.(choice4(my_explicit0~label:"add"(sequence3(required~label:"vmmdev"utf8_string)(required~label:"pid"int)(required~label:"network"(sequence_of(sequence2(required~label:"bridge"utf8_string)(required~label:"tap"utf8_string))))))(my_explicit1~label:"remove"null)(my_explicit2~label:"subscribe"null)(my_explicit3~label:"initial"null))letold_name=letflist=matchName.of_string(String.concat"."list)with|Error(`Msgmsg)->Asn.S.error(`Parsemsg)|Okname->nameandg=Name.to_listinAsn.S.(mapfg(sequence_ofutf8_string))letname=letfstr=matchName.of_stringstrwith|Error(`Msgmsg)->Asn.S.error(`Parsemsg)|Okname->nameandg=Name.to_stringinAsn.S.(mapfgutf8_string)lettyp=letf=function|`C1()->`Solo5|`C2()->Asn.S.parse_error"typ not yet supported"andg=function|`Solo5->`C1()inAsn.S.mapfg@@Asn.S.(choice2(my_explicit0~label:"solo5"null)(my_explicit1~label:"placeholder"null))letfail_behaviour=letf=function|`C1()->`Quit|`C2xs->letexit_codes=matchxswith|[]->None|xs->Some(IS.of_listxs)in`Restartexit_codesandg=function|`Quit->`C1()|`Restartxs->letexit_codes=matchxswith|None->[]|Somei->IS.elementsiin`C2exit_codesinAsn.S.mapfg@@Asn.S.(choice2(my_explicit0~label:"quit"null)(my_explicit1~label:"restart-exit-codes"(set_ofint)))(* this is part of the state file! *)letv0_unikernel_config=letimage=letf=function|`C1x->`Hvt_amd64,x|`C2x->`Hvt_arm64,x|`C3x->`Hvt_amd64_compressed,xandg=function|`Hvt_amd64,x->`C1x|`Hvt_arm64,x->`C2x|`Hvt_amd64_compressed,x->`C3xinAsn.S.mapfg@@Asn.S.(choice3(my_explicit0~label:"hvt-amd64"octet_string)(my_explicit1~label:"hvt-arm64"octet_string)(my_explicit2~label:"hvt-amd64-compressed"octet_string))inletopenUnikernelinletf(cpuid,memory,block_device,network_interfaces,image,argv)=letbridges=matchnetwork_interfaceswithNone->[]|Somexs->List.map(funn->n,None,None)xsandblock_devices=matchblock_devicewithNone->[]|Someb->[(b,None,None)]inlettyp=`Solo5andcompressed=matchfstimagewith`Hvt_amd64_compressed->true|_->falseandimage=sndimageandfail_behaviour=`Quit(* TODO maybe set to restart by default :) *)in{typ;compressed;image;fail_behaviour;cpuid;memory;block_devices;bridges;argv}andg_unikernel=failwith"cannot encode v0 unikernel configs"inAsn.S.mapfg@@Asn.S.(sequence6(required~label:"cpu"int)(required~label:"memory"int)(optional~label:"block"utf8_string)(optional~label:"network-interfaces"(sequence_ofutf8_string))(required~label:"image"image)(optional~label:"arguments"(sequence_ofutf8_string)))(* this is part of the state file (and unikernel_create command)
be aware if this (or a dependent grammar) is changed! *)letv1_unikernel_config=letopenUnikernelinletf(typ,(compressed,(image,(fail_behaviour,(cpuid,(memory,(blocks,(bridges,argv))))))))=letbridges=matchbridgeswithNone->[]|Somexs->List.map(funb->b,None,None)xsandblock_devices=matchblockswithNone->[]|Somexs->List.map(funb->b,None,None)xsin{typ;compressed;image;fail_behaviour;cpuid;memory;block_devices;bridges;argv}andg_unikernel=failwith"cannot encode v1 unikernel configs"inAsn.S.(mapfg@@sequence@@(required~label:"typ"typ)@(required~label:"compressed"bool)@(required~label:"image"octet_string)@(required~label:"fail-behaviour"fail_behaviour)@(required~label:"cpuid"int)@(required~label:"memory"int)@(optional~label:"blocks"(my_explicit0(set_ofutf8_string)))@(optional~label:"bridges"(my_explicit1(set_ofutf8_string)))-@(optional~label:"arguments"(my_explicit2(sequence_ofutf8_string))))letv2_unikernel_config=letopenUnikernelinletf(typ,(compressed,(image,(fail_behaviour,(cpuid,(memory,(blocks,(bridges,argv))))))))=letbridges=matchbridgeswithNone->[]|Somexs->List.map(fun(a,b)->(a,b,None))xsandblock_devices=matchblockswithNone->[]|Somexs->List.map(funb->b,None,None)xsin{typ;compressed;image;fail_behaviour;cpuid;memory;block_devices;bridges;argv}andg(unikernel:config)=letbridges=matchunikernel.bridgeswith|[]->None|xs->Some(List.map(fun(a,b,_)->a,b)xs)andblocks=matchunikernel.block_deviceswith|[]->None|xs->Some(List.map(fun(a,_,_)->a)xs)in(unikernel.typ,(unikernel.compressed,(unikernel.image,(unikernel.fail_behaviour,(unikernel.cpuid,(unikernel.memory,(blocks,(bridges,unikernel.argv))))))))inAsn.S.(mapfg@@sequence@@(required~label:"typ"typ)@(required~label:"compressed"bool)@(required~label:"image"octet_string)@(required~label:"fail-behaviour"fail_behaviour)@(required~label:"cpuid"int)@(required~label:"memory"int)@(optional~label:"blocks"(my_explicit0(set_ofutf8_string)))@(optional~label:"bridges"(my_explicit1(sequence_of(sequence2(required~label:"netif"utf8_string)(optional~label:"bridge"utf8_string)))))-@(optional~label:"arguments"(my_explicit2(sequence_ofutf8_string))))letunikernel_config=letopenUnikernelinletf(typ,(compressed,(image,(fail_behaviour,(cpuid,(memory,(blocks,(bridges,argv))))))))=letbridges=matchbridgeswithNone->[]|Somexs->xsandblock_devices=matchblockswithNone->[]|Somexs->xsin{typ;compressed;image;fail_behaviour;cpuid;memory;block_devices;bridges;argv}andg(unikernel:config)=letbridges=matchunikernel.bridgeswith[]->None|xs->Somexsandblocks=matchunikernel.block_deviceswith[]->None|xs->Somexsin(unikernel.typ,(unikernel.compressed,(unikernel.image,(unikernel.fail_behaviour,(unikernel.cpuid,(unikernel.memory,(blocks,(bridges,unikernel.argv))))))))inAsn.S.(mapfg@@sequence@@(required~label:"typ"typ)@(required~label:"compressed"bool)@(required~label:"image"octet_string)@(required~label:"fail-behaviour"fail_behaviour)@(required~label:"cpuid"int)@(required~label:"memory"int)@(optional~label:"blocks"(my_explicit0(set_of(sequence3(required~label:"block-name"utf8_string)(optional~label:"block-device-name"utf8_string)(optional~label:"block-sector-size"int)))))@(optional~label:"bridges"(my_explicit1(set_of(sequence3(required~label:"netif"utf8_string)(optional~label:"bridge"utf8_string)(optional~label:"mac"mac_addr)))))-@(optional~label:"arguments"(my_explicit2(sequence_ofutf8_string))))letunikernel_cmd=letf=function|`C1`C1()->`Old_unikernel_info1|`C1`C2unikernel->`Unikernel_createunikernel|`C1`C3unikernel->`Unikernel_force_createunikernel|`C1`C4()->`Unikernel_destroy|`C1`C5unikernel->`Unikernel_createunikernel|`C1`C6unikernel->`Unikernel_force_createunikernel|`C2`C1()->`Old_unikernel_get|`C2`C2()->`Old_unikernel_info2|`C2`C3()->`Unikernel_get0|`C2`C4unikernel->`Unikernel_createunikernel|`C2`C5unikernel->`Unikernel_force_createunikernel|`C2`C6level->`Unikernel_getlevel|`C3`C1()->`Unikernel_restart|`C3`C2()->`Unikernel_infoandg=function|`Old_unikernel_info1->`C1(`C1())|`Unikernel_createunikernel->`C2(`C4unikernel)|`Unikernel_force_createunikernel->`C2(`C5unikernel)|`Unikernel_destroy->`C1(`C4())|`Old_unikernel_get->`C2(`C1())|`Old_unikernel_info2->`C2(`C2())|`Unikernel_getlevel->`C2(`C6level)|`Unikernel_restart->`C3(`C1())|`Unikernel_info->`C3(`C2())inAsn.S.mapfg@@Asn.S.(choice3(choice6(my_explicit0~label:"info-OLD1"null)(my_explicit1~label:"create-OLD1"v1_unikernel_config)(my_explicit2~label:"force-create-OLD1"v1_unikernel_config)(my_explicit3~label:"destroy"null)(my_explicit4~label:"create-OLD2"v2_unikernel_config)(my_explicit5~label:"force-create-OLD2"v2_unikernel_config))(choice6(my_explicit6~label:"get-OLD"null)(my_explicit7~label:"info-OLD2"null)(my_explicit8~label:"get-OLD2"null)(my_explicit9~label:"create"unikernel_config)(my_explicit10~label:"force-create"unikernel_config)(my_explicit11~label:"get"int))(choice2(my_explicit12~label:"restart"null)(my_explicit13~label:"info"null)))letpolicy_cmd=letf=function|`C1()->`Policy_info|`C2policy->`Policy_addpolicy|`C3()->`Policy_removeandg=function|`Policy_info->`C1()|`Policy_addpolicy->`C2policy|`Policy_remove->`C3()inAsn.S.mapfg@@Asn.S.(choice3(my_explicit0~label:"info"null)(my_explicit1~label:"add"policy)(my_explicit2~label:"remove"null))letblock_cmd=letf=function|`C1()->`Block_info|`C2size->`Block_add(size,false,None)|`C3()->`Block_remove|`C4(size,compress,data)->`Block_add(size,compress,data)|`C5(compress,data)->`Block_set(compress,data)|`C6level->`Block_dumplevelandg=function|`Block_info->`C1()|`Block_add(size,compress,data)->`C4(size,compress,data)|`Block_remove->`C3()|`Block_set(compress,data)->`C5(compress,data)|`Block_dumplevel->`C6levelinAsn.S.mapfg@@Asn.S.(choice6(my_explicit0~label:"info"null)(my_explicit1~label:"add-OLD"int)(my_explicit2~label:"remove"null)(my_explicit3~label:"add"(sequence3(required~label:"size"int)(required~label:"compress"bool)(optional~label:"data"octet_string)))(my_explicit4~label:"set"(sequence2(required~label:"compress"bool)(required~label:"data"octet_string)))(my_explicit5~label:"dump"int))letwire_command=letf=function|`C1console->`Console_cmdconsole|`C2stats->`Stats_cmdstats|`C3()->Asn.S.parse_error"support for log dropped"|`C4unikernel->`Unikernel_cmdunikernel|`C5policy->`Policy_cmdpolicy|`C6block->`Block_cmdblockandg=function|`Console_cmdc->`C1c|`Stats_cmdc->`C2c|`Unikernel_cmdc->`C4c|`Policy_cmdc->`C5c|`Block_cmdc->`C6cinAsn.S.mapfg@@Asn.S.(choice6(my_explicit0~label:"console"console_cmd)(my_explicit1~label:"statistics"stats_cmd)(my_explicit2~label:"log"null)(my_explicit3~label:"unikernel"unikernel_cmd)(my_explicit4~label:"policy"policy_cmd)(my_explicit5~label:"block"block_cmd))letdata=letf=function|`C1(timestamp,data)->`Utc_console_data(timestamp,data)|`C2(ru,ifs,vmm,mem)->`Stats_data(ru,mem,vmm,ifs)|`C3()->Asn.S.parse_error"support for log was dropped"|`C4(timestamp,data)->`Console_data(timestamp,data)andg=function|`Utc_console_data(timestamp,data)->`C1(timestamp,data)|`Console_data(timestamp,data)->`C4(timestamp,data)|`Stats_data(ru,mem,ifs,vmm)->`C2(ru,vmm,ifs,mem)inAsn.S.mapfg@@Asn.S.(choice4(my_explicit0~label:"utc-console"(sequence2(required~label:"timestamp"utc_time)(required~label:"data"utf8_string)))(my_explicit1~label:"statistics"(sequence4(required~label:"resource-usage"ru)(required~label:"ifdata"(sequence_ofifdata))(optional~label:"vmm-stats"@@my_explicit0(sequence_of(sequence2(required~label:"key"utf8_string)(required~label:"value"int64))))(optional~label:"kinfo-mem"@@implicit1kinfo_mem)))(my_explicit2~label:"log"null)(my_explicit3~label:"console"(sequence2(required~label:"timestamp"generalized_time)(required~label:"data"utf8_string))))letold_unikernel_info=letopenUnikernelinletf(typ,(fail_behaviour,(cpuid,(memory,(digest,(blocks,(bridges,argv)))))))=letbridges=matchbridgeswithNone->[]|Somexs->xsandblock_devices=matchblockswithNone->[]|Somexs->xsandstarted=Ptime.epochin{typ;fail_behaviour;cpuid;memory;block_devices;bridges;argv;digest;started}andg(unikernel:info)=letbridges=matchunikernel.bridgeswith[]->None|xs->Somexsandblocks=matchunikernel.block_deviceswith[]->None|xs->Somexsin(unikernel.typ,(unikernel.fail_behaviour,(unikernel.cpuid,(unikernel.memory,(unikernel.digest,(blocks,(bridges,unikernel.argv)))))))inAsn.S.(mapfg@@sequence@@(required~label:"typ"typ)@(required~label:"fail-behaviour"fail_behaviour)@(required~label:"cpuid"int)@(required~label:"memory"int)@(required~label:"digest"octet_string)@(optional~label:"blocks"(my_explicit0(set_of(sequence3(required~label:"block-name"utf8_string)(optional~label:"block-device-name"utf8_string)(optional~label:"block-sector-size"int)))))@(optional~label:"bridges"(my_explicit1(set_of(sequence3(required~label:"net-name"utf8_string)(optional~label:"bridge-name"utf8_string)(optional~label:"mac"mac_addr)))))-@(optional~label:"arguments"(my_explicit2(sequence_ofutf8_string))))letunikernel_info=letopenUnikernelinletf(typ,(fail_behaviour,(cpuid,(memory,(digest,(blocks,(bridges,(argv,started))))))))=letbridges=matchbridgeswithNone->[]|Somexs->xsandblock_devices=matchblockswithNone->[]|Somexs->xsandstarted=Option.value~default:Ptime.epochstartedin{typ;fail_behaviour;cpuid;memory;block_devices;bridges;argv;digest;started}andg(unikernel:info)=letbridges=matchunikernel.bridgeswith[]->None|xs->Somexsandblocks=matchunikernel.block_deviceswith[]->None|xs->Somexsin(unikernel.typ,(unikernel.fail_behaviour,(unikernel.cpuid,(unikernel.memory,(unikernel.digest,(blocks,(bridges,(unikernel.argv,Someunikernel.started))))))))inAsn.S.(mapfg@@sequence@@(required~label:"typ"typ)@(required~label:"fail-behaviour"fail_behaviour)@(required~label:"cpuid"int)@(required~label:"memory"int)@(required~label:"digest"octet_string)@(optional~label:"blocks"(my_explicit0(set_of(sequence3(required~label:"block-name"utf8_string)(optional~label:"block-device-name"utf8_string)(optional~label:"block-sector-size"int)))))@(optional~label:"bridges"(my_explicit1(set_of(sequence3(required~label:"net-name"utf8_string)(optional~label:"bridge-name"utf8_string)(optional~label:"mac"mac_addr)))))@(optional~label:"arguments"(my_explicit2(sequence_ofutf8_string)))-@(optional~label:"started"(my_explicit3generalized_time)))letheadername=letf(version,sequence,name)={version;sequence;name}andg{version;sequence;name}=version,sequence,nameinAsn.S.mapfg@@Asn.S.(sequence3(required~label:"version"version)(required~label:"sequence"int64)(required~label:"name"name))letsuccessname=letf=function|`C1`C1()->`Empty|`C1`C2str->`Stringstr|`C1`C3policies->`Policiespolicies|`C1`C4unikernels->`Old_unikernelsunikernels|`C1`C5blocks->`Block_devicesblocks|`C1`C6unikernels->`Old_unikernel_infounikernels|`C2`C1(c,i)->`Unikernel_image(c,i)|`C2`C2(compress,data)->`Block_device_image(compress,data)|`C2`C3unikernels->`Unikernel_infounikernelsandg=function|`Empty->`C1(`C1())|`Strings->`C1(`C2s)|`Policiesps->`C1(`C3ps)|`Old_unikernelsunikernels->`C1(`C4unikernels)|`Block_devicesblocks->`C1(`C5blocks)|`Old_unikernel_infounikernels->`C1(`C6unikernels)|`Unikernel_image(c,i)->`C2(`C1(c,i))|`Block_device_image(compress,data)->`C2(`C2(compress,data))|`Unikernel_infounikernels->`C2(`C3unikernels)inAsn.S.mapfg@@Asn.S.(choice2(choice6(my_explicit0~label:"empty"null)(my_explicit1~label:"string"utf8_string)(my_explicit2~label:"policies"(sequence_of(sequence2(required~label:"name"name)(required~label:"policy"policy))))(my_explicit3~label:"unikernels-OLD"(sequence_of(sequence2(required~label:"name"name)(required~label:"config"v2_unikernel_config))))(my_explicit4~label:"block-devices"(sequence_of(sequence3(required~label:"name"name)(required~label:"size"int)(required~label:"active"bool))))(my_explicit5~label:"old-unikernel-info"(sequence_of(sequence2(required~label:"name"name)(required~label:"info"old_unikernel_info)))))(choice3(my_explicit6~label:"unikernel-image"(sequence2(required~label:"compressed"bool)(required~label:"image"octet_string)))(my_explicit7~label:"block-device-image"(sequence2(required~label:"compressed"bool)(required~label:"image"octet_string)))(my_explicit8~label:"unikernel-info"(sequence_of(sequence2(required~label:"name"name)(required~label:"info"unikernel_info))))))letpayloadname=letf=function|`C1cmd->`Commandcmd|`C2s->`Successs|`C3str->`Failurestr|`C4data->`Datadataandg=function|`Commandcmd->`C1cmd|`Successs->`C2s|`Failurestr->`C3str|`Datad->`C4dinAsn.S.mapfg@@Asn.S.(choice4(my_explicit0~label:"command"wire_command)(my_explicit1~label:"reply"(successname))(my_explicit2~label:"failure"utf8_string)(my_explicit3~label:"data"data))letwirename=Asn.S.(sequence2(required~label:"header"(headername))(required~label:"payload"(payloadname)))letwire_of_str,wire_to_str=letdec,enc=projections_of(wirename)anddec_old,enc_old=projections_of(wireold_name)in(funbuf->let*version=decode_wire_versionbufinmatchversionwith|`AV3|`AV4->dec_oldbuf|`AV5->decbuf),(fun(header,payload)->matchheader.versionwith|`AV3|`AV4->enc_old(header,payload)|`AV5->enc(header,payload))lettriee=letfelts=List.fold_left(funtrie(key,value)->matchName.of_stringkeywith|Error(`Msgm)->invalid_argm|Okname->lettrie,ret=Vmm_trie.insertnamevaluetrieinassert(ret=None);trie)Vmm_trie.emptyeltsandgtrie=List.map(fun(k,v)->Name.to_stringk,v)(Vmm_trie.alltrie)inAsn.S.mapfg@@Asn.S.(sequence_of(sequence2(required~label:"name"utf8_string)(required~label:"value"e)))letversion0_unikernels=triev0_unikernel_configletversion1_unikernels=triev1_unikernel_configletversion2_unikernels=triev2_unikernel_configletversion3_unikernels=trieunikernel_configletpolicies=triepolicyletstate=(* the choice is the implicit version + migration... be aware when
any dependent data layout changes .oO(/o\) *)letf=function|`C1data->data,Vmm_trie.empty|`C2data->data,Vmm_trie.empty|`C3data->data,Vmm_trie.empty|`C4data->data,Vmm_trie.empty|`C5(data,policies)->(data,policies)andg(unikernels,policies)=`C5(unikernels,policies)inAsn.S.mapfg@@Asn.S.(choice5(my_explicit0~label:"unikernel-OLD1"version1_unikernels)(my_explicit1~label:"unikernel-OLD0"version0_unikernels)(my_explicit2~label:"unikernel-OLD2"version2_unikernels)(my_explicit3~label:"unikernel"version3_unikernels)(my_explicit4~label:"unikernel and policy"(sequence2(required~label:"unikernels"version3_unikernels)(required~label:"policies"policies))))letstate_of_str,state_to_str=letd,e=projections_ofstateind,fununikps->e(unik,ps)letcert_extension=(* note that subCAs are deployed out there, thus modifying the encoding of
commands may break them. *)Asn.S.(sequence2(required~label:"version"version)(required~label:"command"wire_command))letof_cert_extension,to_cert_extension=leta,b=projections_ofcert_extensionina,(fund->b(current,d))