1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069(* (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|x->Asn.S.parse_error"unknown version number 0x%X"xandg=function|`AV5->5inAsn.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|`C1m->`Console_addm|`C2`C1ts->`Console_subscribe(`Sincets)|`C2`C2c->`Console_subscribe(`Countc)|`C3()->`Console_list_inactiveandg=function|`Console_addm->`C1m|`Console_subscribe`Sincets->`C2(`C1ts)|`Console_subscribe`Countc->`C2(`C2c)|`Console_list_inactive->`C3()inAsn.S.mapfg@@Asn.S.(choice3(my_explicit0~label:"add"int)(my_explicit2~label:"subscribe"(choice2(my_explicit0~label:"since"generalized_time)(my_explicit1~label:"count"int)))(my_explicit3~label:"inactive consoles"null))(* 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))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=sndimageandstartup=Noneandfail_behaviour=`Quit(* TODO maybe set to restart by default :) *)andadd_name=truein{typ;compressed;image;fail_behaviour;startup;add_name;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)xsandstartup=Noneandadd_name=truein{typ;compressed;image;fail_behaviour;startup;add_name;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)xsandstartup=Noneandadd_name=truein{typ;compressed;image;fail_behaviour;startup;add_name;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))))letv3_unikernel_config=letopenUnikernelinletf(typ,(compressed,(image,(fail_behaviour,(cpuid,(memory,(blocks,(bridges,argv))))))))=letbridges=matchbridgeswithNone->[]|Somexs->xsandblock_devices=matchblockswithNone->[]|Somexs->xsandstartup=Noneandadd_name=truein{typ;compressed;image;fail_behaviour;startup;add_name;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_config=letopenUnikernelinletf(typ,(compressed,(image,(startup,(add_name,(fail_behaviour,(cpuid,(memory,(blocks,(bridges,argv))))))))))=letbridges=matchbridgeswithNone->[]|Somexs->xsandblock_devices=matchblockswithNone->[]|Somexs->xsin{typ;compressed;image;fail_behaviour;startup;add_name;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.startup,(unikernel.add_name,(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)@(optional~label:"startup"int)@(required~label:"add_name"bool)@(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_arguments=letopenUnikernelinletf(fail_behaviour,(startup,(add_name,(cpuid,(memory,(blocks,(bridges,argv)))))))=letbridges=matchbridgeswithNone->[]|Somexs->xsandblock_devices=matchblockswithNone->[]|Somexs->xsin{fail_behaviour;startup;add_name;cpuid;memory;block_devices;bridges;argv}andg(unikernel:arguments)=letbridges=matchunikernel.bridgeswith[]->None|xs->Somexsandblocks=matchunikernel.block_deviceswith[]->None|xs->Somexsin(unikernel.fail_behaviour,(unikernel.startup,(unikernel.add_name,(unikernel.cpuid,(unikernel.memory,(blocks,(bridges,unikernel.argv)))))))inAsn.S.(mapfg@@sequence@@(required~label:"fail-behaviour"fail_behaviour)@(optional~label:"startup"int)@(required~label:"add_name"bool)@(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_arguments_old=letopenUnikernelinletf(fail_behaviour,(cpuid,(memory,(blocks,(bridges,argv)))))=letbridges=matchbridgeswithNone->[]|Somexs->xsandblock_devices=matchblockswithNone->[]|Somexs->xsandstartup=Noneandadd_name=truein{fail_behaviour;startup;add_name;cpuid;memory;block_devices;bridges;argv}andg(unikernel:arguments)=letbridges=matchunikernel.bridgeswith[]->None|xs->Somexsandblocks=matchunikernel.block_deviceswith[]->None|xs->Somexsin(unikernel.fail_behaviour,(unikernel.cpuid,(unikernel.memory,(blocks,(bridges,unikernel.argv)))))inAsn.S.(mapfg@@sequence@@(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()->`Unikernel_destroy|`C1`C2unikernel->`Unikernel_createunikernel|`C1`C3unikernel->`Unikernel_force_createunikernel|`C1`C4level->`Unikernel_getlevel|`C1`C5()->`Unikernel_restartNone|`C1`C6()->`Old_unikernel_info3|`C2`C1`C1()->`Unikernel_restartNone|`C2`C1`C2args->`Unikernel_restart(Someargs)|`C2`C1`C3args->`Unikernel_restart(Someargs)|`C2`C2()->`Old_unikernel_info4|`C2`C3unikernel->`Unikernel_createunikernel|`C2`C4unikernel->`Unikernel_force_createunikernel|`C2`C5()->`Unikernel_infoandg=function|`Unikernel_createunikernel->`C2(`C3unikernel)|`Unikernel_force_createunikernel->`C2(`C4unikernel)|`Unikernel_destroy->`C1(`C1())|`Unikernel_getlevel->`C1(`C4level)|`Unikernel_restartNone->`C2(`C1(`C1()))|`Unikernel_restart(Someargs)->`C2(`C1(`C3args))|`Old_unikernel_info3->`C1(`C6())|`Old_unikernel_info4->`C2(`C2())|`Unikernel_info->`C2(`C5())inAsn.S.mapfg@@Asn.S.(choice2(choice6(my_explicit3~label:"destroy"null)(my_explicit9~label:"create-OLD"v3_unikernel_config)(my_explicit10~label:"force-create-OLD"v3_unikernel_config)(my_explicit11~label:"get"int)(my_explicit12~label:"restart-OLD"null)(my_explicit13~label:"info-OLD3"null))(choice5(my_explicit14~label:"restart"(choice3(my_explicit0~label:"no arguments"null)(my_explicit1~label:"new arguments OLD"unikernel_arguments_old)(my_explicit2~label:"new arguments"unikernel_arguments)))(my_explicit15~label:"info-OLD4"null)(my_explicit16~label:"create"unikernel_config)(my_explicit17~label:"force-create"unikernel_config)(my_explicit18~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`C1()->`Block_info|`C1`C2size->`Block_addsize|`C1`C3()->`Block_remove|`C1`C4(size,compress,data)->`Old_block_add(size,compress,data)|`C1`C5(compress,data)->`Old_block_set(compress,data)|`C1`C6level->`Old_block_dumplevel|`C2`C1level->`Block_dumplevel|`C2`C2compress->`Block_setcompressandg=function|`Block_info->`C1(`C1())|`Block_addsize->`C1(`C2size)|`Block_remove->`C1(`C3())|`Old_block_add(size,compress,data)->`C1(`C4(size,compress,data))|`Old_block_set(compress,data)->`C1(`C5(compress,data))|`Old_block_dumplevel->`C1(`C6level)|`Block_dumplevel->`C2(`C1level)|`Block_setcompress->`C2(`C2compress)inAsn.S.mapfg@@Asn.S.(choice2(choice6(my_explicit0~label:"info"null)(my_explicit1~label:"add"int)(my_explicit2~label:"remove"null)(my_explicit3~label:"add-OLD"(sequence3(required~label:"size"int)(required~label:"compress"bool)(optional~label:"data"octet_string)))(my_explicit4~label:"set-OLD"(sequence2(required~label:"compress"bool)(required~label:"data"octet_string)))(my_explicit5~label:"dump"int))(choice2(my_explicit6~label:"dump"int)(my_explicit7~label:"set"bool)))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(ru,ifs,vmm,mem)->`Stats_data(ru,mem,vmm,ifs)|`C2()->Asn.S.parse_error"support for log was dropped"|`C3(timestamp,data)->`Console_data(timestamp,data)|`C4`C1s->`Block_data(Somes)|`C4`C2()->`Block_dataNoneandg=function|`Console_data(timestamp,data)->`C3(timestamp,data)|`Stats_data(ru,mem,ifs,vmm)->`C1(ru,vmm,ifs,mem)|`Block_dataNone->`C4(`C2())|`Block_dataSomes->`C4(`C1s)inAsn.S.mapfg@@Asn.S.(choice4(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)))(my_explicit4~label:"block"(choice2(my_explicit0~label:"some data"octet_string)(my_explicit1~label:"no data"null))))letold_unikernel_info3=letopenUnikernelinletf(typ,(fail_behaviour,(cpuid,(memory,(digest,(blocks,(bridges,(argv,started))))))))=letbridges=matchbridgeswithNone->[]|Somexs->List.map(fun(unikernel_device,host_device,mac)->{unikernel_device;host_device=Option.value~default:unikernel_devicehost_device;(* We can't synthesize the mac, so use a dummy value *)mac=Option.value~default:Macaddr.broadcastmac})xsandblock_devices=matchblockswithNone->[]|Somexs->List.map(fun(unikernel_device,host_device,sector_size)->{unikernel_device;host_device=Option.value~default:unikernel_devicehost_device;sector_size=Option.value~default:512(* TODO: default from solo5-hvt *)sector_size;size=0})xsandstarted=Option.value~default:Ptime.epochstartedandstartup=Nonein{typ;fail_behaviour;startup;cpuid;memory;block_devices;bridges;argv;digest;started}andg(unikernel:info)=letbridges=matchunikernel.bridgeswith|[]->None|xs->Some(List.map(fun{unikernel_device;host_device;mac}->unikernel_device,Somehost_device,Somemac)xs)andblocks=matchunikernel.block_deviceswith|[]->None|xs->Some(List.map(fun{unikernel_device;host_device;sector_size;_}->unikernel_device,Somehost_device,Somesector_size)xs)in(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:"unikernel-device"utf8_string)(optional~label:"host-device"utf8_string)(optional~label:"sector-size"int)))))@(optional~label:"bridges"(my_explicit1(set_of(sequence3(required~label:"unikernel-device"utf8_string)(optional~label:"host-device"utf8_string)(optional~label:"mac"mac_addr)))))@(optional~label:"arguments"(my_explicit2(sequence_ofutf8_string)))-@(optional~label:"started"(my_explicit3generalized_time)))letold_unikernel_info4=letopenUnikernelinletf(typ,(fail_behaviour,(cpuid,(memory,(digest,(blocks,(bridges,(argv,started))))))))=letbridges=matchbridgeswithNone->[]|Somexs->List.map(fun(unikernel_device,host_device,mac)->{unikernel_device;host_device;mac})xsandblock_devices=matchblockswithNone->[]|Somexs->List.map(fun(unikernel_device,host_device,sector_size,size)->{unikernel_device;host_device;sector_size;size})xsandstarted=Option.value~default:Ptime.epochstartedandstartup=Nonein{typ;fail_behaviour;startup;cpuid;memory;block_devices;bridges;argv;digest;started}andg(unikernel:info)=letbridges=matchunikernel.bridgeswith|[]->None|xs->Some(List.map(fun{unikernel_device;host_device;mac}->unikernel_device,host_device,mac)xs)andblocks=matchunikernel.block_deviceswith|[]->None|xs->Some(List.map(fun{unikernel_device;host_device;sector_size;size}->unikernel_device,host_device,sector_size,size)xs)in(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(sequence4(required~label:"unikernel-device"utf8_string)(required~label:"host-device"utf8_string)(required~label:"sector-size"int)(required~label:"block-size"int)))))@(optional~label:"bridges"(my_explicit1(set_of(sequence3(required~label:"unikernel-device"utf8_string)(required~label:"host-device"utf8_string)(required~label:"mac"mac_addr)))))@(optional~label:"arguments"(my_explicit2(sequence_ofutf8_string)))-@(optional~label:"started"(my_explicit3generalized_time)))letunikernel_info=letopenUnikernelinletf(typ,(startup,(fail_behaviour,(cpuid,(memory,(digest,(blocks,(bridges,(argv,started)))))))))=letbridges=matchbridgeswithNone->[]|Somexs->List.map(fun(unikernel_device,host_device,mac)->{unikernel_device;host_device;mac})xsandblock_devices=matchblockswithNone->[]|Somexs->List.map(fun(unikernel_device,host_device,sector_size,size)->{unikernel_device;host_device;sector_size;size})xsandstarted=Option.value~default:Ptime.epochstartedin{typ;fail_behaviour;startup;cpuid;memory;block_devices;bridges;argv;digest;started}andg(unikernel:info)=letbridges=matchunikernel.bridgeswith|[]->None|xs->Some(List.map(fun{unikernel_device;host_device;mac}->unikernel_device,host_device,mac)xs)andblocks=matchunikernel.block_deviceswith|[]->None|xs->Some(List.map(fun{unikernel_device;host_device;sector_size;size}->unikernel_device,host_device,sector_size,size)xs)in(unikernel.typ,(unikernel.startup,(unikernel.fail_behaviour,(unikernel.cpuid,(unikernel.memory,(unikernel.digest,(blocks,(bridges,(unikernel.argv,Someunikernel.started)))))))))inAsn.S.(mapfg@@sequence@@(required~label:"typ"typ)@(optional~label:"startup"int)@(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(sequence4(required~label:"unikernel-device"utf8_string)(required~label:"host-device"utf8_string)(required~label:"sector-size"int)(required~label:"block-size"int)))))@(optional~label:"bridges"(my_explicit1(set_of(sequence3(required~label:"unikernel-device"utf8_string)(required~label:"host-device"utf8_string)(required~label:"mac"mac_addr)))))@(optional~label:"arguments"(my_explicit2(sequence_ofutf8_string)))-@(optional~label:"started"(my_explicit3generalized_time)))letheader=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))letsuccess=letf=function|`C1`C1()->`Empty|`C1`C2str->`Stringstr|`C1`C3policies->`Policiespolicies|`C1`C4blocks->`Block_devicesblocks|`C1`C5(c,i)->`Unikernel_image(c,i)|`C1`C6(compress,data)->`Old_block_device_image(compress,data)|`C2`C1unikernels->`Old_unikernel_info3unikernels|`C2`C2unikernels->`Old_unikernel_info4unikernels|`C2`C3compress->`Block_device_imagecompress|`C2`C4unikernels->`Unikernel_infounikernels|`C2`C5names->`Consolesnamesandg=function|`Empty->`C1(`C1())|`Strings->`C1(`C2s)|`Policiesps->`C1(`C3ps)|`Block_devicesblocks->`C1(`C4blocks)|`Unikernel_image(c,i)->`C1(`C5(c,i))|`Old_block_device_image(compress,data)->`C1(`C6(compress,data))|`Old_unikernel_info3unikernels->`C2(`C1unikernels)|`Old_unikernel_info4unikernels->`C2(`C2unikernels)|`Block_device_imagecompress->`C2(`C3compress)|`Unikernel_infounikernels->`C2(`C4unikernels)|`Consolesnames->`C2(`C5names)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_explicit4~label:"block-devices"(sequence_of(sequence3(required~label:"name"name)(required~label:"size"int)(required~label:"active"bool))))(my_explicit6~label:"unikernel-image"(sequence2(required~label:"compressed"bool)(required~label:"image"octet_string)))(my_explicit7~label:"old-block-device-image"(sequence2(required~label:"compressed"bool)(required~label:"image"octet_string))))(choice5(my_explicit8~label:"old-unikernel-info3"(sequence_of(sequence2(required~label:"name"name)(required~label:"info"old_unikernel_info3))))(my_explicit9~label:"old-unikernel-info4"(sequence_of(sequence2(required~label:"name"name)(required~label:"info"old_unikernel_info4))))(my_explicit10~label:"block-device-image"bool)(my_explicit11~label:"unikernel-info"(sequence_of(sequence2(required~label:"name"name)(required~label:"info"unikernel_info))))(my_explicit12~label:"consoles"(sequence_ofname))))letpayload=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"success)(my_explicit2~label:"failure"utf8_string)(my_explicit3~label:"data"data))letwire=Asn.S.(sequence2(required~label:"header"header)(required~label:"payload"payload))letwire_of_str,wire_to_str=letdec,enc=projections_ofwirein(funbuf->let*version=decode_wire_versionbufinmatchversionwith|`AV5->decbuf),(fun(header,payload)->matchheader.versionwith|`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=triev3_unikernel_configletversion4_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)|`C6(data,policies)->(data,policies)andg(unikernels,policies)=`C6(unikernels,policies)inAsn.S.mapfg@@Asn.S.(choice6(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-OLD3"version3_unikernels)(my_explicit4~label:"unikernel and policy OLD"(sequence2(required~label:"unikernels"version3_unikernels)(required~label:"policies"policies)))(my_explicit5~label:"unikernel and policy"(sequence2(required~label:"unikernels"version4_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))