Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file dhcp_server.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937(*
* Copyright (c) 2015-2017 Christiano F. Haesbaert <haesbaert@haesbaert.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*)moduleConfig=structopenSexplib.ConvopenSexplib.Stdtypehost={hostname:string;options:Dhcp_wire.dhcp_optionlist;fixed_addr:Ipaddr_sexp.V4.toption;hw_addr:Macaddr_sexp.t;}[@@derivingsexp]typet={options:Dhcp_wire.dhcp_optionlist;hostname:string;default_lease_time:int32;max_lease_time:int32;ip_addr:Ipaddr_sexp.V4.t;mac_addr:Macaddr_sexp.t;network:Ipaddr_sexp.V4.Prefix.t;range:(Ipaddr_sexp.V4.t*Ipaddr_sexp.V4.t)option;hosts:hostlist;}[@@derivingsexp]lett1_time_ratio=0.5lett2_time_ratio=0.8letlease_time_goodconfigtime=time<=config.max_lease_timeletsanity_checkconfig=(* Check if fixed addresses make sense *)List.iter(funhost->matchhost.fixed_addrwith|None->()|Someaddr->ifnot(Ipaddr.V4.Prefix.memaddrconfig.network)theninvalid_arg(Printf.sprintf"Fixed address %s does not \
belong to subnet %s"(Ipaddr.V4.to_stringaddr)(Ipaddr.V4.Prefix.to_stringconfig.network))elsematchconfig.rangewith|None->()|Somerange->ifUtil.addr_in_rangeaddrrangethenletlow=fstrangeinlethigh=sndrangeininvalid_arg(Printf.sprintf"Fixed address %s must be \
outside of range %s:%s"(Ipaddr.V4.to_stringaddr)(Ipaddr.V4.to_stringlow)(Ipaddr.V4.to_stringhigh)))config.hosts;configletmake?(hostname="charrua-dhcp-server")?(default_lease_time=60*60*2)(* 2 hours *)?(max_lease_time=60*60*24)(* 24 hours *)?(hosts=[])~addr_tuple~network~range~options=letopenDhcp_wirein(* Try to ensure the user doesn't pass bad options *)let()=List.iter(function|Subnet_mask_|Renewal_t1_|Rebinding_t2_|Client_id_|Ip_lease_time_|End|Pad|Request_ip_|Parameter_requests_asoption->invalid_arg(Printf.sprintf"option %s is not allowed"(dhcp_option_to_stringoption))|_->())optionsin(* Prepend a Subnet_mask, since we can always infer that from the network,
the user doesn't need to specify, it must always come first in case there
is a Router option later on RFC2132 3.3 *)letoptions=Subnet_mask(Ipaddr.V4.Prefix.netmasknetwork)::optionsinletip_addr=fstaddr_tupleinletmac_addr=sndaddr_tupleinsanity_check{options;hostname;default_lease_time=Int32.of_intdefault_lease_time;max_lease_time=Int32.of_intmax_lease_time;ip_addr;mac_addr;network;range;hosts;}letconfig_of_astaddr_tuple(ast:Ast.t)=letip_addr=fstaddr_tupleinletmac_addr=sndaddr_tupleinletsubnets=ast.Ast.subnetsinletsubnet=List.find(funs->Ipaddr.V4.Prefix.memip_addrs.Ast.network)subnetsinlethosts=List.map(funh->{hostname=h.Ast.hostname;options=h.Ast.options;fixed_addr=h.Ast.fixed_addr;hw_addr=h.Ast.hw_addr;})subnet.Ast.hostsinletdefault_lease_time=Util.some_or_defaultsubnet.Ast.default_lease_timeast.Ast.default_lease_timeinletmax_lease_time=Util.some_or_defaultsubnet.Ast.max_lease_timeast.Ast.max_lease_timeinletnetwork=subnet.Ast.networkin(* Prepend a Subnet_mask, since we can always infer that from the network,
the user doesn't need to specify, it must always come first in case there
is a Router option later on, RFC2132 3.3. subnet.Ast.options must come
first, this way we make sure we hit the more specific option when
searching for a single entry. *)letoptions=Dhcp_wire.Subnet_mask(Ipaddr.V4.Prefix.netmasknetwork)::(subnet.Ast.options@ast.Ast.options)insanity_check{options;hostname="charrua-dhcp-server";(* XXX Implement server-name option. *)default_lease_time;max_lease_time;ip_addr;mac_addr;network=subnet.Ast.network;range=subnet.Ast.range;hosts=hosts;}letparseconfigtxtaddr_tuple=letchokelexs=letopenLexinginletpos=lex.lex_curr_pinletstr=Printf.sprintf"%s at line %d around `%s`"spos.pos_lnum(Lexing.lexemelex)ininvalid_argstrinletlex=Lexing.from_stringconfigtxtinletast=tryDhcp_parser.mainDhcp_lexer.lexlexwith|Dhcp_parser.Error->chokelex"Parser Error"|Invalid_argumente->chokelexeinconfig_of_astaddr_tupleastendmoduleLease=structopenSexplib.ConvopenSexplib.StdmoduleClient_id=structopenDhcp_wiretypet=client_idletcompareab=matcha,bwith|Hwaddrmaca,Hwaddrmacb->Macaddr.comparemacamacb|Idida,Ididb->String.compareidaidb|Id_,Hwaddr_->-1|Hwaddr_,Id_->1endmoduleAddr_map=Map.Make(Ipaddr.V4)moduleId_map=Map.Make(Client_id)(* Lease (dhcp bindings) operations *)typet={tm_start:int32;tm_end:int32;addr:Ipaddr_sexp.V4.t;client_id:Dhcp_wire.client_id;}[@@derivingsexp]letto_stringlease=Sexplib.Sexp.to_string_hum(sexp_of_tlease)(* Database, collection of leases *)typedatabase={id_map:tId_map.t;addr_map:tAddr_map.t;}(* with sexp *)letupdate_dbid_mapaddr_map={id_map;addr_map}letmake_db()=update_dbId_map.emptyAddr_map.emptyletto_listdb=Id_map.fold(funidleasel->lease::l)db.id_map[]letmakeclient_idaddr~duration~now=lettm_start=nowinlettm_end=Int32.addtm_startdurationin{tm_start;tm_end;addr;client_id}letmake_fixedmacaddr~duration~now=make(Dhcp_wire.Hwaddrmac)addr~duration~nowlettimeleftlease~now=letleft=Int32.sublease.tm_endnowinifleft<Int32.zerothenInt32.zeroelseleftlettimeleft_exnlease~now=letleft=timeleftlease~nowinifleft=Int32.zerotheninvalid_arg"No time left for lease"elseleftlettimeleft3leaset1_ratiot2_ratio~now=letleft=Int32.to_float(timeleftlease~now)in(Int32.of_floatleft,Int32.of_float(left*.t1_ratio),Int32.of_float(left*.t2_ratio))letextendlease~now=letoriginal=Int32.sublease.tm_endlease.tm_startinmakelease.client_idlease.addr~duration:original~nowletexpiredlease~now=timeleftlease~now=Int32.zeroletgarbage_collectdb~now=update_db(Id_map.filter(fun_lease->not(expiredlease~now))db.id_map)(Addr_map.filter(fun_lease->not(expiredlease~now))db.addr_map)letlease_of_client_idclient_iddb=Util.find_some@@fun()->Id_map.findclient_iddb.id_mapletlease_of_addraddrdb=Util.find_some@@fun()->Addr_map.findaddrdb.addr_mapletremoveleasedb=update_db(Id_map.removelease.client_iddb.id_map)(Addr_map.removelease.addrdb.addr_map)letreplaceleasedb=(* First clear both maps *)letclr_map=matchlease_of_addrlease.addrdbwith|Somel->removeldb|None->dbinletclr_map=matchlease_of_client_idlease.client_idclr_mapwith|Somel->removeldb|None->dbinletclr_map=removeleaseclr_mapinupdate_db(Id_map.addlease.client_idleaseclr_map.id_map)(Addr_map.addlease.addrleaseclr_map.addr_map)letaddr_allocatedaddrdb=Util.true_if_some@@lease_of_addraddrdbletaddr_availableaddrdb~now=matchlease_of_addraddrdbwith|None->true|Somelease->expiredlease~now(*
* We try to use the last 4 bytes of the mac address as a hint for the ip
* address, if that fails, we try a linear search.
*)letget_usable_addriddbrange~now=matchrangewith|None->None|Somerange->letlow_ip,high_ip=rangeinletlow_32=Ipaddr.V4.to_int32low_ipinlethigh_32=Ipaddr.V4.to_int32high_ipinif(Int32.comparelow_32high_32)>0theninvalid_arg"invalid range, must be (low * high)";lethint_ip=letv=matchidwith|Dhcp_wire.Ids->Int32.of_int1805(* XXX who cares *)|Dhcp_wire.Hwaddrhw->lets=String.sub(Macaddr.to_byteshw)24inletb0=Int32.shift_left(Char.codes.[3]|>Int32.of_int)0inletb1=Int32.shift_left(Char.codes.[2]|>Int32.of_int)8inletb2=Int32.shift_left(Char.codes.[1]|>Int32.of_int)16inletb3=Int32.shift_left(Char.codes.[0]|>Int32.of_int)24inInt32.zero|>Int32.logorb0|>Int32.logorb1|>Int32.logorb2|>Int32.logorb3inInt32.remv(Int32.sub(Int32.succhigh_32)low_32)|>Int32.abs|>Int32.addlow_32|>Ipaddr.V4.of_int32inletreclinear_loopofff=letip=Ipaddr.V4.of_int32(Int32.addlow_32off)iniffipthenSomeipelseifoff=high_32thenNoneelselinear_loop(Int32.succoff)finifnot(addr_allocatedhint_ipdb)thenSomehint_ipelsematchlinear_loopInt32.zero(funa->not(addr_allocatedadb))with|Someip->Someip|None->linear_loopInt32.zero(funa->addr_availableadb~now)endmoduleInput=structopenConfigopenDhcp_wireletbad_packetfmt=Printf.ksprintf(funs->invalid_args)fmttyperesult=|Silence|UpdateofLease.database|ReplyofDhcp_wire.pkt*Lease.database|Warningofstring|Errorofstringlethost_of_macconfigmac=Util.find_some@@fun()->List.find(funhost->host.hw_addr=mac)config.hostsletfixed_addr_of_macconfigmac=matchhost_of_macconfigmacwith|Somehost->ifhost.hw_addr=macthenhost.fixed_addrelseNone|None->Noneletoptions_of_macconfigmac=matchhost_of_macconfigmacwith|Somehost->host.options|None->[]letfind_leaseconfigclient_idmacdb~now=match(fixed_addr_of_macconfigmac)with|Somefixed_addr->Some(Lease.make_fixedmacfixed_addr~duration:config.default_lease_time~now),true|None->Lease.lease_of_client_idclient_iddb,falseletgood_addressconfigmacaddrdb=match(fixed_addr_of_macconfigmac)with(* If this is a fixed address, it's good if mac matches ip. *)|Somefixed_addr->addr=fixed_addr|None->(matchconfig.rangewith|None->false|Somerange->Util.addr_in_rangeaddrrange)letmake_replyconfigreqpkt~ciaddr~yiaddr~siaddr~giaddroptions=letop=BOOTREPLYinlethtype=Ethernet_10mbinlethlen=6inlethops=0inletxid=reqpkt.xidinletsecs=0inletflags=reqpkt.flagsinletchaddr=reqpkt.chaddrinletsname=config.hostnameinletfile=""in(* Build the frame header *)letdstport=ifgiaddr=Ipaddr.V4.unspecifiedthenclient_portelseserver_portinletsrcport=server_portinletsrcmac=config.mac_addrinletdstmac,dstip=match(find_message_typeoptions)with|None->failwith"make_reply: No msgtype in options"|Somem->matchmwith|DHCPNAK->ifgiaddr<>Ipaddr.V4.unspecifiedthen(reqpkt.srcmac,giaddr)else(Macaddr.broadcast,Ipaddr.V4.broadcast)|DHCPOFFER|DHCPACK->ifgiaddr<>Ipaddr.V4.unspecifiedthen(reqpkt.srcmac,giaddr)elseifciaddr<>Ipaddr.V4.unspecifiedthen(reqpkt.srcmac,ciaddr)elseifflags=Unicastthen(reqpkt.srcmac,yiaddr)else(Macaddr.broadcast,Ipaddr.V4.broadcast)|_->invalid_arg("Can't send message type "^(msgtype_to_stringm))inletsrcip=config.ip_addrin{srcmac;dstmac;srcip;dstip;srcport;dstport;op;htype;hlen;hops;xid;secs;flags;ciaddr;yiaddr;siaddr;giaddr;chaddr;sname;file;options}letfor_usconfigpkt=pkt.dstport=Dhcp_wire.server_port&&pkt.srcport=Dhcp_wire.client_port&&(pkt.dstmac=config.mac_addr||pkt.dstmac=Macaddr.broadcast)&&(pkt.dstip=config.ip_addr||pkt.dstip=Ipaddr.V4.broadcast)letvalid_pktpkt=ifpkt.op<>BOOTREQUESTthenfalseelseifpkt.htype<>Ethernet_10mbthenfalseelseifpkt.hlen<>6thenfalseelseifpkt.hops<>0thenfalseelsetrue(* might be slow O(preqs * options) *)letreplies_of_optionsoptionspreqs=(* Sort parameter requests to guarantee ordering. *)letpreqs=List.sort(funab->compare(option_code_to_inta)(option_code_to_intb))preqsinletunassigned_options=List.filter(functionUnassigned(_,_)->true|_->false)optionsin(* matches multiple options *)letmfnfnr=matchfnoptionswith|[]->None|l->Some(fnrl)in(* matches the first single option *)letsfnfnr=matchfnoptionswith|Somex->Some(fnrx)|None->Noneinletconsider=function|SUBNET_MASK->sfind_subnet_mask(funx->Subnet_maskx)|TIME_OFFSET->sfind_time_offset(funx->Time_offsetx)|ROUTERS->mcollect_routers(funx->Routersx)|TIME_SERVERS->mcollect_time_servers(funx->Time_serversx)|NAME_SERVERS->mcollect_name_servers(funx->Name_serversx)|DNS_SERVERS->mcollect_dns_servers(funx->Dns_serversx)|LOG_SERVERS->mcollect_log_servers(funx->Log_serversx)|COOKIE_SERVERS->mcollect_cookie_servers(funx->Cookie_serversx)|LPR_SERVERS->mcollect_lpr_servers(funx->Lpr_serversx)|IMPRESS_SERVERS->mcollect_impress_servers(funx->Impress_serversx)|RSCLOCATION_SERVERS->mcollect_rsc_location_servers(funx->Rsclocation_serversx)|HOSTNAME->sfind_hostname(funx->Hostnamex)|BOOTFILE_SIZE->sfind_bootfile_size(funx->Bootfile_sizex)|MERIT_DUMPFILE->sfind_merit_dumpfile(funx->Merit_dumpfilex)|DOMAIN_NAME->sfind_domain_name(funx->Domain_namex)|SWAP_SERVER->sfind_swap_server(funx->Swap_serverx)|ROOT_PATH->sfind_root_path(funx->Root_pathx)|EXTENSION_PATH->sfind_extension_path(funx->Extension_pathx)|IPFORWARDING->sfind_ipforwarding(funx->Ipforwardingx)|NLSR->sfind_nlsr(funx->Nlsrx)|POLICY_FILTERS->mcollect_policy_filters(funx->Policy_filtersx)|MAX_DATAGRAM->sfind_max_datagram(funx->Max_datagramx)|DEFAULT_IP_TTL->sfind_default_ip_ttl(funx->Default_ip_ttlx)|PMTU_AGEING_TIMO->sfind_pmtu_ageing_timo(funx->Pmtu_ageing_timox)|PMTU_PLATEAU_TABLE->sfind_pmtu_plateau_table(funx->Pmtu_plateau_tablex)|INTERFACE_MTU->sfind_interface_mtu(funx->Interface_mtux)|ALL_SUBNETS_LOCAL->sfind_all_subnets_local(funx->All_subnets_localx)|BROADCAST_ADDR->sfind_broadcast_addr(funx->Broadcast_addrx)|PERFORM_MASK_DISCOVERY->sfind_perform_mask_discovery(funx->Perform_router_discx)|MASK_SUPPLIER->sfind_mask_supplier(funx->Mask_supplierx)|PERFORM_ROUTER_DISC->sfind_perform_router_disc(funx->Perform_router_discx)|ROUTER_SOL_ADDR->sfind_router_sol_addr(funx->Router_sol_addrx)|STATIC_ROUTES->mcollect_static_routes(funx->Static_routesx)|TRAILER_ENCAPSULATION->sfind_trailer_encapsulation(funx->Trailer_encapsulationx)|ARP_CACHE_TIMO->sfind_arp_cache_timo(funx->Arp_cache_timox)|ETHERNET_ENCAPSULATION->sfind_ethernet_encapsulation(funx->Ethernet_encapsulationx)|TCP_DEFAULT_TTL->sfind_tcp_default_ttl(funx->Tcp_default_ttlx)|TCP_KEEPALIVE_INTERVAL->sfind_tcp_keepalive_interval(funx->Tcp_keepalive_intervalx)|TCP_KEEPALIVE_GARBAGE->sfind_tcp_keepalive_garbage(funx->Tcp_keepalive_garbagex)|NIS_DOMAIN->sfind_nis_domain(funx->Nis_domainx)|NIS_SERVERS->mcollect_nis_servers(funx->Nis_serversx)|NTP_SERVERS->mcollect_ntp_servers(funx->Ntp_serversx)|VENDOR_SPECIFIC->sfind_vendor_specific(funx->Vendor_specificx)|NETBIOS_NAME_SERVERS->mcollect_netbios_name_servers(funx->Netbios_name_serversx)|NETBIOS_DATAGRAM_DISTRIB_SERVERS->mcollect_netbios_datagram_distrib_servers(funx->Netbios_datagram_distrib_serversx)|NETBIOS_NODE->sfind_netbios_node(funx->Netbios_nodex)|NETBIOS_SCOPE->sfind_netbios_scope(funx->Netbios_scopex)|XWINDOW_FONT_SERVERS->mcollect_xwindow_font_servers(funx->Xwindow_font_serversx)|XWINDOW_DISPLAY_MANAGERS->mcollect_xwindow_display_managers(funx->Xwindow_display_managersx)|REQUEST_IP->None(* Previously included *)|IP_LEASE_TIME->None(* Previously included *)|OPTION_OVERLOAD->sfind_option_overload(funx->Option_overloadx)|MESSAGE_TYPE->None(* Senseless *)|SERVER_IDENTIFIER->None(* Previously included *)|PARAMETER_REQUESTS->None(* Senseless *)|MESSAGE->sfind_message(funx->Messagex)|MAX_MESSAGE->sfind_max_message(funx->Max_messagex)|RENEWAL_T1->None(* Previously included *)|REBINDING_T2->None(* Previously included *)|VENDOR_CLASS_ID->sfind_vendor_class_id(funx->Vendor_class_idx)|CLIENT_ID->None(* Senseless *)|NETWARE_IP_DOMAIN->sfind_netware_ip_domain(funx->Netware_ip_domainx)|NETWARE_IP_OPTION->sfind_netware_ip_option(funx->Netware_ip_optionx)|NIS_PLUS_DOMAIN->sfind_nis_plus_domain(funx->Nis_plus_domainx)|NIS_PLUS_SERVERS->mcollect_nis_plus_servers(funx->Nis_plus_serversx)|TFTP_SERVER_NAME->sfind_tftp_server_name(funx->Tftp_server_namex)|BOOTFILE_NAME->sfind_bootfile_name(funx->Bootfile_namex)|MOBILE_IP_HOME_AGENT->mcollect_mobile_ip_home_agent(funx->Mobile_ip_home_agentx)|SMTP_SERVERS->mcollect_smtp_servers(funx->Smtp_serversx)|POP3_SERVERS->mcollect_pop3_servers(funx->Pop3_serversx)|NNTP_SERVERS->mcollect_nntp_servers(funx->Nntp_serversx)|WWW_SERVERS->mcollect_www_servers(funx->Www_serversx)|FINGER_SERVERS->mcollect_finger_servers(funx->Finger_serversx)|IRC_SERVERS->mcollect_irc_servers(funx->Irc_serversx)|STREETTALK_SERVERS->mcollect_streettalk_servers(funx->Streettalk_serversx)|STREETTALK_DA->mcollect_streettalk_da(funx->Streettalk_dax)|USER_CLASS->sfind_user_class(funx->User_classx)|DIRECTORY_AGENT->sfind_directory_agent(funx->Directory_agentx)|SERVICE_SCOPE->sfind_service_scope(funx->Service_scopex)|RAPID_COMMIT->sfind_rapid_commit(fun_->Rapid_commit)|CLIENT_FQDN->sfind_client_fqdn(funx->Client_fqdnx)|RELAY_AGENT_INFORMATION->sfind_relay_agent_information(funx->Relay_agent_informationx)|ISNS->sfind_isns(funx->Isnsx)|NDS_SERVERS->sfind_nds_servers(funx->Nds_serversx)|NDS_TREE_NAME->sfind_nds_tree_name(funx->Nds_tree_namex)|NDS_CONTEXT->sfind_nds_context(funx->Nds_contextx)|BCMCS_CONTROLLER_DOMAIN_NAME_LIST->sfind_bcmcs_controller_domain_name(funx->Bcmcs_controller_domain_name_listx)|BCMCS_CONTROLLER_IPV4_ADDR->mcollect_bcmcs_controller_ipv4_addrs(funx->Bcmcs_controller_ipv4_addrsx)|AUTHENTICATION->sfind_authentication(funx->Authenticationx)|CLIENT_LAST_TRANSACTION_TIME->sfind_client_last_transaction_time(funx->Client_last_transaction_timex)|ASSOCIATED_IPS->mcollect_associated_ips(funx->Associated_ipsx)|CLIENT_SYSTEM->sfind_client_system(funx->Client_systemx)|CLIENT_NDI->sfind_client_ndi(funx->Client_ndix)|LDAP->sfind_ldap(funx->Ldapx)|UUID_GUID->sfind_uuid_guid(funx->Uuid_guidx)|USER_AUTH->sfind_user_auth(funx->User_authx)|GEOCONF_CIVIC->sfind_geoconf_civic(funx->Geoconf_civicx)|PCODE->sfind_pcode(funx->Pcodex)|TCODE->sfind_tcode(funx->Tcodex)|NETINFO_ADDRESS->sfind_netinfo_address(funx->Netinfo_addressx)|NETINFO_TAG->sfind_netinfo_tag(funx->Netinfo_tagx)|URL->sfind_url(funx->Urlx)|AUTO_CONFIG->sfind_auto_config(funx->Auto_configx)|NAME_SERVICE_SEARCH->sfind_name_service_search(funx->Name_service_searchx)|SUBNET_SELECTION->sfind_subnet_selection(funx->Subnet_selectionx)|DOMAIN_SEARCH->sfind_domain_search(funx->Domain_searchx)|SIP_SERVERS->sfind_sip_servers(funx->Sip_serversx)|CLASSLESS_STATIC_ROUTE->sfind_classless_static_route(funx->Classless_static_routex)|CCC->sfind_ccc(funx->Cccx)|GEOCONF->sfind_geoconf(funx->Geoconfx)|VI_VENDOR_CLASS->sfind_vi_vendor_class(funx->Vi_vendor_classx)|VI_VENDOR_INFO->sfind_vi_vendor_info(funx->Vi_vendor_infox)|PXE_128->sfind_pxe_128(funx->Pxe_128x)|PXE_129->sfind_pxe_129(funx->Pxe_129x)|PXE_130->sfind_pxe_130(funx->Pxe_130x)|PXE_131->sfind_pxe_131(funx->Pxe_131x)|PXE_132->sfind_pxe_132(funx->Pxe_132x)|PXE_133->sfind_pxe_133(funx->Pxe_133x)|PXE_134->sfind_pxe_134(funx->Pxe_134x)|PXE_135->sfind_pxe_135(funx->Pxe_135x)|PANA_AGENT->sfind_pana_agent(funx->Pana_agentx)|V4_LOST->sfind_v4_lost(funx->V4_lostx)|CAPWAP_AC_V4->sfind_capwap_ac_v4(funx->Capwap_ac_v4x)|IPV4_ADDRESS_MOS->sfind_ipv4_address_mos(funx->Ipv4_address_mosx)|IPV4_FQDN_MOS->sfind_ipv4_fqdn_mos(funx->Ipv4_fqdn_mosx)|SIP_UA_DOMAINS->sfind_sip_ua_domains(funx->Sip_ua_domainsx)|IPV4_ADDRESS_ANDSF->sfind_ipv4_address_andsf(funx->Ipv4_address_andsfx)|GEOLOCK->sfind_geolock(funx->Geolockx)|FORCENEW_NONCE_CAPABLE->sfind_forcenew_nonce_capable(funx->Forcenew_nonce_capablex)|RDNSS_SELECTION->sfind_rdnss_selection(funx->Rdnss_selectionx)|MISC_150->sfind_misc_150(funx->Misc_150x)|STATUS_CODE->sfind_status_code(funx->Status_codex)|ABSOLUTE_TIME->sfind_absolute_time(funx->Absolute_timex)|START_TIME_OF_STATE->sfind_start_time_of_state(funx->Start_time_of_statex)|QUERY_START_TIME->sfind_query_end_time(funx->Query_start_timex)|QUERY_END_TIME->sfind_query_end_time(funx->Query_end_timex)|DHCP_STATE->sfind_dhcp_state(funx->Dhcp_statex)|DATA_SOURCE->sfind_data_source(funx->Data_sourcex)|V4_PCP_SERVER->sfind_v4_pcp_server(funx->V4_pcp_serverx)|V4_PORTPARAMS->sfind_v4_portparams(funx->V4_portparamsx)|DHCP_CAPTIVE_PORTAL->sfind_dhcp_captive_portal(funx->Dhcp_captive_portalx)|ETHERBOOT_175->sfind_etherboot_175(funx->Etherboot_175x)|IP_TELEFONE->sfind_ip_telefone(funx->Ip_telefonex)|ETHERBOOT_177->sfind_etherboot_177(funx->Etherboot_177x)|PXE_LINUX->sfind_pxe_linux(funx->Pxe_linuxx)|CONFIGURATION_FILE->sfind_configuration_file(funx->Configuration_filex)|PATH_PREFIX->sfind_path_prefix(funx->Path_prefixx)|REBOOT_TIME->sfind_reboot_time(funx->Reboot_timex)|OPTION_6RD->sfind_option_6rd(funx->Option_6rdx)|V4_ACCESS_DOMAIN->sfind_v4_access_domain(funx->V4_access_domainx)|SUBNET_ALLOCATION->sfind_subnet_allocation(funx->Subnet_allocationx)|VIRTUAL_SUBNET_SELECTION->sfind_virtual_subnet_selection(funx->Virtual_subnet_selectionx)|WEB_PROXY_AUTO_DISC->sfind_web_proxy_auto_disc(funx->Web_proxy_auto_discx)|PRIVATE_CLASSLESS_STATIC_ROUTE->sfind_private_classless_static_route(funx->Private_classless_static_routex)|UNASSIGNED_84|UNASSIGNED_96|UNASSIGNED_102|UNASSIGNED_103|UNASSIGNED_104|UNASSIGNED_105|UNASSIGNED_106|UNASSIGNED_107|UNASSIGNED_108|UNASSIGNED_109|UNASSIGNED_110|UNASSIGNED_111|UNASSIGNED_115|UNASSIGNED_126|UNASSIGNED_127|UNASSIGNED_143|UNASSIGNED_147|UNASSIGNED_148|UNASSIGNED_149|UNASSIGNED_161|UNASSIGNED_162|UNASSIGNED_163|UNASSIGNED_164|UNASSIGNED_165|UNASSIGNED_166|UNASSIGNED_167|UNASSIGNED_168|UNASSIGNED_169|UNASSIGNED_170|UNASSIGNED_171|UNASSIGNED_172|UNASSIGNED_173|UNASSIGNED_174|UNASSIGNED_178|UNASSIGNED_179|UNASSIGNED_180|UNASSIGNED_181|UNASSIGNED_182|UNASSIGNED_183|UNASSIGNED_184|UNASSIGNED_185|UNASSIGNED_186|UNASSIGNED_187|UNASSIGNED_188|UNASSIGNED_189|UNASSIGNED_190|UNASSIGNED_191|UNASSIGNED_192|UNASSIGNED_193|UNASSIGNED_194|UNASSIGNED_195|UNASSIGNED_196|UNASSIGNED_197|UNASSIGNED_198|UNASSIGNED_199|UNASSIGNED_200|UNASSIGNED_201|UNASSIGNED_202|UNASSIGNED_203|UNASSIGNED_204|UNASSIGNED_205|UNASSIGNED_206|UNASSIGNED_207|UNASSIGNED_214|UNASSIGNED_215|UNASSIGNED_216|UNASSIGNED_217|UNASSIGNED_218|UNASSIGNED_219|UNASSIGNED_222|UNASSIGNED_223|RESERVED_224|RESERVED_225|RESERVED_226|RESERVED_227|RESERVED_228|RESERVED_229|RESERVED_230|RESERVED_231|RESERVED_232|RESERVED_233|RESERVED_234|RESERVED_235|RESERVED_236|RESERVED_237|RESERVED_238|RESERVED_239|RESERVED_240|RESERVED_241|RESERVED_242|RESERVED_243|RESERVED_244|RESERVED_245|RESERVED_246|RESERVED_247|RESERVED_248|RESERVED_250|RESERVED_251|RESERVED_253|RESERVED_254ascode->find_option(functionUnassigned(c,s)asuwhenc=code->Someu|_->None)unassigned_options|PAD|END->None(* Senseless *)inUtil.filter_mapconsiderpreqsletcollect_repliesconfigmacpreqs=matchhost_of_macconfigmacwith|Somehost->replies_of_options(host.options@config.options)preqs|None->replies_of_optionsconfig.optionspreqsletcollect_replies_test=collect_repliesletinput_decline_releaseconfigdbpktnow=letopenUtilinletmsgtype=matchfind_message_typepkt.optionswith|Somemsgtype->msgtype_to_stringmsgtype|None->failwith"Unexpected message type"inletourip=config.ip_addrinletreqip=find_request_ippkt.optionsinletsidip=find_server_identifierpkt.optionsinletclient_id=client_id_of_pktpktinmatchsidipwith|None->bad_packet"%s without server identifier"msgtype|Somesidip->ifourip<>sidipthenSilence(* not for us *)elsematchreqipwith|None->bad_packet"%s without request ip"msgtype|Somereqip->(* check if the lease is actually his *)letlease,fixed_lease=find_leaseconfigclient_idpkt.chaddrdb~nowinmatchleasewith|None->Silence(* lease is unowned, ignore *)|Somelease->Update(ifnotfixed_leasethenLease.removeleasedbelsedb)letinput_decline=input_decline_releaseletinput_release=input_decline_releaseletinput_informconfigdbpkt=ifpkt.ciaddr=Ipaddr.V4.unspecifiedthenbad_packet"DHCPINFORM without ciaddr"elseletourip=config.ip_addrinletoptions=letopenUtilincons(Message_typeDHCPACK)@@cons(Server_identifierourip)@@cons_if_some_f(find_vendor_class_idpkt.options)(funvid->Vendor_class_idvid)@@match(find_parameter_requestspkt.options)with|Somepreqs->collect_repliesconfigpkt.chaddrpreqs|None->[]inletpkt=make_replyconfigpkt~ciaddr:pkt.ciaddr~yiaddr:Ipaddr.V4.unspecified~siaddr:ourip~giaddr:pkt.giaddroptionsinReply(pkt,db)letinput_requestconfigdbpktnow=letclient_id=client_id_of_pktpktinletlease,fixed_lease=find_leaseconfigclient_idpkt.chaddrdb~nowinletourip=config.ip_addrinletreqip=find_request_ippkt.optionsinletsidip=find_server_identifierpkt.optionsinletnak?msg()=letopenUtilinletoptions=cons(Message_typeDHCPNAK)@@cons(Server_identifierourip)@@cons_if_some_fmsg(funmsg->Messagemsg)@@cons_if_some_f(find_client_idpkt.options)(funid->Client_idid)@@cons_if_some_f(find_vendor_class_idpkt.options)(funvid->Vendor_class_idvid)[]inletpkt=make_replyconfigpkt~ciaddr:Ipaddr.V4.unspecified~yiaddr:Ipaddr.V4.unspecified~siaddr:Ipaddr.V4.unspecified~giaddr:pkt.giaddroptionsinReply(pkt,db)inletack?(renew=false)lease=letopenUtilinletlease=ifrenewthenLease.extendlease~nowelseleaseinletlease_time,t1,t2=Lease.timeleft3leaseConfig.t1_time_ratioConfig.t2_time_ratio~nowinletoptions=cons(Message_typeDHCPACK)@@cons(Ip_lease_timelease_time)@@cons(Renewal_t1t1)@@cons(Rebinding_t2t2)@@cons(Server_identifierourip)@@cons_if_some_f(find_vendor_class_idpkt.options)(funvid->Vendor_class_idvid)@@match(find_parameter_requestspkt.options)with|Somepreqs->collect_repliesconfigpkt.chaddrpreqs|None->[]inletreply=make_replyconfigpkt~ciaddr:pkt.ciaddr~yiaddr:lease.Lease.addr~siaddr:ourip~giaddr:pkt.giaddroptionsinifnotfixed_leasethenlet()=assert(lease.Lease.client_id=client_id)inReply(reply,Lease.replaceleasedb)elseReply(reply,db)inmatchsidip,reqip,leasewith|Somesidip,Somereqip,_->(* DHCPREQUEST generated during SELECTING state *)ifsidip<>ouripthen(* is it for us ? *)Silenceelseifpkt.ciaddr<>Ipaddr.V4.unspecifiedthen(* violates RFC2131 4.3.2 *)Warning"Bad DHCPREQUEST, ciaddr is not 0"elseifnot(good_addressconfigpkt.chaddrreqipdb)thennak~msg:"Requested address is not in subnet range"()else(matchleasewith|Somelease->ifLease.expiredleasenow&¬(Lease.addr_availablereqipdb~now)thennak~msg:"Lease has expired and address is taken"()elseiflease.Lease.addr<>reqipthennak~msg:"Requested address is incorrect"()elseack~renew:truelease|None->ifnot(Lease.addr_availablereqipdb~now)thennak~msg:"Requested address is not available"()elseack(Lease.makeclient_idreqip~duration:config.default_lease_time~now))|None,Somereqip,Somelease->(* DHCPREQUEST @ INIT-REBOOT state *)ifpkt.ciaddr<>Ipaddr.V4.unspecifiedthen(* violates RFC2131 4.3.2 *)bad_packet"Bad DHCPREQUEST, ciaddr is not 0"elseifLease.expiredlease~now&¬(Lease.addr_availablereqipdb~now)thennak~msg:"Lease has expired and address is taken"()(* TODO check if it's in the correct network when giaddr <> 0 *)elseifpkt.giaddr=Ipaddr.V4.unspecified&¬(good_addressconfigpkt.chaddrreqipdb)thennak~msg:"Requested address is not in subnet range"()elseiflease.Lease.addr<>reqipthennak~msg:"Requested address is incorrect"()elseacklease|None,None,Somelease->(* DHCPREQUEST @ RENEWING/REBINDING state *)ifpkt.ciaddr=Ipaddr.V4.unspecifiedthen(* violates RFC2131 4.3.2 renewal *)bad_packet"Bad DHCPREQUEST, ciaddr is not 0"elseifLease.expiredlease~now&¬(Lease.addr_availablelease.Lease.addrdb~now)thennak~msg:"Lease has expired and address is taken"()elseiflease.Lease.addr<>pkt.ciaddrthennak~msg:"Requested address is incorrect"()elseack~renew:truelease|_->Silenceletdiscover_addrconfigleasedbpktnow=letid=client_id_of_pktpktinmatchleasewith(* Handle the case where we have a lease *)|Somelease->ifnot(Lease.expiredlease~now)thenSomelease.Lease.addr(* If the lease expired, the address might not be available *)elseif(Lease.addr_availablelease.Lease.addrdb~now)thenSomelease.Lease.addrelseLease.get_usable_addriddbconfig.range~now(* Handle the case where we have no lease *)|None->match(find_request_ippkt.options)with|Somereq_addr->if(good_addressconfigpkt.chaddrreq_addrdb)&&(Lease.addr_availablereq_addrdb~now)thenSomereq_addrelseLease.get_usable_addriddbconfig.range~now|None->Lease.get_usable_addriddbconfig.range~nowletdiscover_lease_timeconfigleasedbpktnow=match(find_ip_lease_timepkt.options)with|Someip_lease_time->ifConfig.lease_time_goodconfigip_lease_timethenip_lease_timeelseconfig.default_lease_time|None->matchleasewith|None->config.default_lease_time|Somelease->ifLease.expiredlease~nowthenconfig.default_lease_timeelseLease.timeleftlease~nowletinput_discoverconfigdbpktnow=(* RFC section 4.3.1 *)(* Figure out the ip address *)letid=client_id_of_pktpktinletlease,fixed_lease=find_leaseconfigidpkt.chaddrdb~nowinletourip=config.ip_addrinletaddr=discover_addrconfigleasedbpktnowin(* Figure out the lease lease_time *)letlease_time=discover_lease_timeconfigleasedbpktnowinmatchaddrwith|None->Warning"No ips left to offer"|Someaddr->letopenUtilin(* Start building the options *)lett1=Int32.of_float(Config.t1_time_ratio*.(Int32.to_floatlease_time))inlett2=Int32.of_float(Config.t2_time_ratio*.(Int32.to_floatlease_time))inletoptions=cons(Message_typeDHCPOFFER)@@cons(Ip_lease_timelease_time)@@cons(Renewal_t1t1)@@cons(Rebinding_t2t2)@@cons(Server_identifierourip)@@cons_if_some_f(find_vendor_class_idpkt.options)(funvid->Vendor_class_idvid)@@match(find_parameter_requestspkt.options)with|Somepreqs->collect_repliesconfigpkt.chaddrpreqs|None->[]inletpkt=make_replyconfigpkt~ciaddr:Ipaddr.V4.unspecified~yiaddr:addr~siaddr:ourip~giaddr:pkt.giaddroptionsinReply(pkt,db)letinput_pktconfigdbpkttime=tryifnot(for_usconfigpkt)thenSilenceelseifvalid_pktpktthenmatchfind_message_typepkt.optionswith|SomeDHCPDISCOVER->input_discoverconfigdbpkttime|SomeDHCPREQUEST->input_requestconfigdbpkttime|SomeDHCPDECLINE->input_declineconfigdbpkttime|SomeDHCPRELEASE->input_releaseconfigdbpkttime|SomeDHCPINFORM->input_informconfigdbpkt|None->bad_packet"Malformed packet: no dhcp msgtype"|Somem->Warning("Unhandled msgtype "^(msgtype_to_stringm))elsebad_packet"Invalid packet"with|Invalid_argumente->Erroreend