a 'Dg֏@sddlZddlZddlZddlZddlZddlZddlZddlZddlZddl m Z ddl m Z ddl mZddlmZmZmZmZmZmZddlZddlmZmZmZddlmZmZeeZ dZ!d Z"d Z#d Z$Gd d d e%Z&Gddde&Z'Gddde&Z(Gddde&Z)d%ee*efdddZ+ddZ,d&ddZ-d'ddZ.Gdddej/Z0Gdd d e0Z1Gd!d"d"e0Z2Gd#d$d$e0Z3e2e1e3gZ4dS)(N)suppress)StringIO)TimeoutExpired)AnyCallableDictListOptionalTuple)subp temp_utilsutil)get_interface_macis_ib_interfacez/run/systemd/netif/leasesz/var/lib/dhclientz .+\.leases?$aN#!/bin/sh log() { echo "udhcpc[$PPID]" "$interface: $2" } [ -z "$1" ] && echo "Error: should be called from udhcpc" && exit 1 case $1 in bound|renew) cat < "$LEASE_FILE" { "interface": "$interface", "fixed-address": "$ip", "subnet-mask": "$subnet", "routers": "${router%% *}", "static_routes" : "${staticroutes}" } JSON ;; deconfig) log err "Not supported" exit 1 ;; leasefail | nak) log err "configuration failed: $1: $message" exit 1 ;; *) echo "$0: Unknown udhcpc command: $1" >&2 exit 1 ;; esac c@seZdZdZdS)NoDHCPLeaseErrorz'Raised when unable to get a DHCP lease.N__name__ __module__ __qualname____doc__rr6/usr/lib/python3.9/site-packages/cloudinit/net/dhcp.pyrAsrc@seZdZdZdS)InvalidDHCPLeaseFileErrorzRaised when parsing an empty or invalid dhclient.lease file. Current uses are DataSourceAzure and DataSourceEc2 during ephemeral boot to scrape metadata. NrrrrrrEsrc@seZdZdZdS)NoDHCPLeaseInterfaceErrorz7Raised when unable to find a viable interface for DHCP.NrrrrrrMsrc@seZdZdZdS)NoDHCPLeaseMissingDhclientErrorz$Raised when unable to find dhclient.NrrrrrrQsrreturncCs2|p|j}|dur"tdt|j|||S)aPerform dhcp discovery if nic valid and dhclient command exists. If the nic is invalid or undiscoverable or dhclient command is not found, skip dhcp_discovery and return an empty dict. @param nic: Name of the network interface we want to run dhclient on. @param dhcp_log_func: A callable accepting the dhclient output and error streams. @return: A list of dicts representing dhcp options for each lease obtained from the dhclient discovery if run, otherwise an empty list is returned. Nz1Skip dhcp_discovery: Unable to find fallback nic.)Zfallback_interfaceLOGdebugrZ dhcp_clientdhcp_discovery)distroZnic dhcp_log_func interfacerrrmaybe_perform_dhcp_discoveryUs   r#cCsttjt|ddS)zParse a systemd lease file content as in /run/systemd/netif/leases/ Parse this (almost) ini style file even though it says: # This is private data. Do not parse. Simply return a dictionary of key/values.F)Z list_values)dict configobjZ ConfigObjr)contentrrrnetworkd_parse_leaselsr'cCsP|dur t}i}tj|s |St|D] }tttj||||<q*|S)zReturn a dictionary of dictionaries representing each lease found in lease_d.i The top level key will be the filename, which is typically the ifindex.N) NETWORKD_LEASES_DIRospathisdirlistdirr'r load_text_filejoin)leases_dretZlfilerrrnetworkd_load_leasesws  r1cCsF|dur t}t|d}t|D]\}}||r"||Sq"dS)N)r/)r(r1sorteditemsget)Zkeynamer/ZleasesZ_ifindexdatarrrnetworkd_get_option_from_leasess  r6c@seZdZdZdZddZeddZeddZee d d d Z ee d d d Z e j e ee efdddZee j e eee e fdddZe j de eeee efdddZdS) DhcpClient cCst|j|_|jstdSN)r which client_namedhcp_client_pathrselfrrr__init__szDhcpClient.__init__cCstjd|jgddgddS)NZpkillrZrcs)r r<)clsrrrkill_dhcp_clientszDhcpClient.kill_dhcp_clientcCs*|td}|D]}t|qdS)Nz/var/lib/dhcp/*)rDglobr)remove)rCfilesfilerrr clear_leasess zDhcpClient.clear_leases)dhcp_interfacecCs|jd|j|ddgddS)NstartrrArBZmanage_servicer<rCrJr rrr start_serviceszDhcpClient.start_servicecCs|jd|jddgddS)NstoprrArBrLrMrrr stop_serviceszDhcpClient.stop_servicer"rcCsiS)zGet the most recent lease from the ephemeral phase as a dict. Return a dict of dhcp options. The dict contains key value pairs from the most recent lease. rr?r"rrrget_newest_leaseszDhcpClient.get_newest_leaseroutesrcCsgS)ap parse classless static routes from string The tuple is composed of the network_address (including net length) and gateway for a parsed static route. @param routes: string containing classless static routes @returns: list of tuple(str, str) for all valid parsed routes until the first parsing error. r)rUrrrparse_static_routess zDhcpClient.parse_static_routesNr"r!rcCsiS)aRun dhcp client on the interface without scripts or filesystem artifacts. @param interface: Name of the network interface on which to send a dhcp request @param dhcp_log_func: A callable accepting the client output and error streams. @param distro: a distro object for network interface manipulation @return: dict of lease options representing the most recent dhcp lease parsed from the dhclient.lease file r)r?r"r!r rrrrszDhcpClient.dhcp_discovery)NN)rrrr<timeoutr@ classmethodrDrIstrrNrPabcabstractmethodrrrS staticmethodrr rVr rrrrrrr7s0    r7cseZdZdZfddZeeeeee fdddZ eddZ eeee fd d d Z dee eeee fd ddZeeeeeefdddZee edddZedddZZS) IscDhclientZdhclientcstd|_dS)Nz/run/dhclient.leasesuperr@ lease_filer> __class__rrr@s zIscDhclient.__init__) lease_contentrcCstdtj}g}t|dkr"gS||D]t}g}|dD]2}|dddd}|r>||ddq>t |}| d }|rt ||d <||q,|S) zparse the content of a lease file @param lease_content: a string containing the contents of an isc-dhclient lease @return: a list of leases, most recent last zlease {(?P.*?)}\nr;"r8zoption  rA unknown-245) recompileDOTALLlenfindallsplitstripreplaceappendr$r4r^get_ip_from_lease_value)rdZ lease_regex dhcp_leasesleaseZ lease_optionslineoptionsopt_245rrr parse_leasess$   zIscDhclient.parse_leasescCsx|dd}t|dkrdd}|dD] }t|dkr>d|}||7}q&tdt|ddd}n |d }t|S) N\r8:rA0z>Lzutf-8) rprlrnstructpackintencodesocket inet_ntoa)Zfallback_lease_valueZunescaped_valueZ hex_stringZhex_pairZ packed_bytesrrrrrs     z#IscDhclient.get_ip_from_lease_valuerQcCs`ttDt|j}|r>||}|r>|dWdSWdn1sR0YiS)aGet the most recent lease from the ephemeral phase as a dict. Return a dict of dhcp options. The dict contains key value pairs from the most recent lease. @param interface: an interface string - not used in this class, but required for function signature compatibility with other classes that require a distro object @raises: InvalidDHCPLeaseFileError on empty or unparsable leasefile content. N)rFileNotFoundErrorr r-rarx)r?r"r&rsrrrrSs   4zIscDhclient.get_newest_leaseNrWc Cstd|d}d}d}t|j|}t|jd}tt&t|t|jWdn1sh0Y|j |t |rdt |dd} d|| f} t jd d } tj| |d }t|| z$t||j|j|||\} } WnDtjy8}z(td |j|j|jt|WYd}~n d}~00tj||jg|dd }|rvtdddd|DiSd}d}d}d}t|D]}zt|}t|}Wn>tyd|d}YnXt yd|d}Yn:0|!|}|dkr$td|t"|t#j$d }q}z-IscDhclient.dhcp_discovery..unknownFr8No PID file found at z, dhclient is still runningPID file contained [z], dhclient is still runningrAzkilling dhclient with pid=%szCdhclient(pid=%s, parentpid=%s) failed to daemonize after %s secondsg$@)*rrrrXrrr)rFranet_opslink_uprrr get_tmp_ancestorr*r.r write_filer Zbuild_dhclient_cmdr=ProcessExecutionError exit_codestderrstdoutrZwait_for_fileswarningranger-ro ValueErrorZ get_proc_ppidkillsignalSIGKILLtimesleeperrorrSr)r?r"r!r pid_fileZ config_file sleep_time sleep_cyclesrZdhcp_client_identifierZinterface_dhclient_contenttmp_dirouterrrmissingZppidZ daemonized pid_content debug_msg_pidrtrrrr&s   *                 zIscDhclient.dhcp_discoveryrTc sB|dddtdD}g}dvr:dd|D}fdd}d }t|D]\}}||krfqRt|}|td d vrd }t||d |kr|||t||d |Sd||d|d} d||d||} ||}n2|tdd vrd}t||d |kr>|||t||d |Sd||d|ddg} d||d||} ||}n|td dvrd}t||d |kr|||t||d |Sd||d|dddg} d||d||} ||}n|tdd vrd}t||d |kr`|||t||d |Sd||d|dgd} d||d||} ||}n|d krd}t||d |kr|||t||d |Sd} d||d||} ||}nt d||S| d| |f| fqR|S)a( parse rfc3442 format and return a list containing tuple of strings. The tuple is composed of the network_address (including net length) and gateway for a parsed static route. It can parse two formats of rfc3442, one from dhcpcd and one from dhclient (isc). @param rfc3442: string in rfc3442 format (isc or dhcpd) @returns: list of tuple(str, str) for all valid parsed routes until the first parsing error. e.g.: sr=parse_static_routes( "32,169,254,169,254,130,56,248,255,0,130,56,240,1") sr=[ ("169.254.169.254/32", "130.56.248.255"), ("0.0.0.0/0", "130.56.240.1") ] sr2 = parse_static_routes( "24.191.168.128 192.168.128.1,0 192.168.128.1") sr2 = [ ("191.168.128.0/24", "192.168.128.1"), ("0.0.0.0/0", "192.168.128.1") ] # unknown-121 option format sr3 = parse_static_routes( "0:a:0:0:1:20:a8:3f:81:10:a:0:0:1:20:a9:fe:a9:fe:a:0:0:1") sr3 = [ ("0.0.0.0/0", "10.0.0.1"), ("168.63.129.16/32", "10.0.0.1"), ("169.254.169.254/32", "10.0.0.1"), ] Python version of isc-dhclient's hooks: /etc/dhcp/dhclient-exit-hooks.d/rfc3442-classless-routes recSsg|] }|r|qSrrrtokrrr rz3IscDhclient.parse_static_routes..z[, . :]r{cSsg|]}tt|dqS)r})rZrrrrrrrcsd|||f}t|dS)NzRFC3442 string malformed. Current route has CIDR of %s and requires %s significant octets, but only %s remain. Verify DHCP rfc3442-classless-static-routes value: %s)rr)ZcidrrequiredZremainmsgZrfc3442rr _trunc_errors  z5IscDhclient.parse_static_routes.._trunc_errorr! N.rArzr|r)r|r|r|z0.0.0.0zSParsed invalid net length "%s". Verify DHCP rfc3442-classless-static-routes value.z%s/%s) rstriprirn enumeraterrrlr.rrrq) rUtokens static_routesrZ current_idxidxrZ net_lengthZreq_toksZ net_addressZgatewayrrrrVs|*     "    zIscDhclient.parse_static_routesrc Csd}|j|jfttffD]\}}|s&qg}zt|}WntyNYqYn0d}|D]<}t||sjqXtj ||}tj |}||krX|}|}qX|r|SqdS)zGet the latest lease file from a distro-managed dhclient Doesn't consider the ephemeral timeframe lease. @param distro: used for distro-specific lease location and filename @return: The most recent lease file, or None Ng) Zdhclient_lease_directoryZdhclient_lease_file_regexDHCLIENT_FALLBACK_LEASE_DIRDHCLIENT_FALLBACK_LEASE_REGEXr)r,rrisearchr*r.getmtime) r Z latest_fileZ directoryZregexZ lease_filesZ latest_mtimefnameZabs_pathmtimerrr!get_newest_lease_file_from_distro%s2       z-IscDhclient.get_newest_lease_file_from_distro)keycCsJ||}|rFt|}|rFt||D]}||}|r*|Sq*dS)a8Get a key from the latest lease from distro-managed dhclient Doesn't consider the ephemeral timeframe lease. @param lease_dir: distro-specific lease to check @param lease_file_regex: distro-specific regex to match lease name @return: The most recent lease file, or None N)rr r-reversedrxr4)r?r rrar&rtZserverrrrget_key_from_latest_leaseQs   z%IscDhclient.get_key_from_latest_lease)NN)rrrr<r@r]rZrrrrxrrrSr rrr rVrr __classcell__rrrbrr^s(   w+r^c@seZdZdZdZdeeeeee fdddZ e e e ee ddd Ze eeed d d Zeeee fd ddZe eeeeefdddZdS)DhcpcdZdhcpcdi,NrWc Cs.td|d}t|j|}g}|j|ztt|r@dg}|jdddddg||}tj||jd \}} |d ur||| | |} | rtg|d j } d } d } d }t |D]}zNt |  } t| }||} | r td|| t| tjWqWndty6td| YqYnHtyTd| d}Yn*tyrd| d}Yn 0| WSt|qt|| WStdWnty}z(td|j|j|j t|WYd }~nLd }~0tjy(}z(td|j|j|j t|WYd }~n d }~00d S)aRun dhcpcd on the interface without scripts/filesystem artifacts. @param interface: Name of the network interface on which to send a dhcp request @param dhcp_log_func: A callable accepting the client output and error streams. @param distro: a distro object for network interface manipulation @return: dict of lease options representing the most recent dhcp lease parsed from the dhclient.lease file rrz --clientid --ipv4onlyz--waitipz --persistentz--noarpz--script=/bin/true)rXNz-PFr8z!killing dhcpcd with pid=%s gid=%sz9Process group id [%s] has already exited, nothing to killrz, dhcpcd is still runningrz], dhcpcd is still runningzNo lease foundz8dhcpcd timed out after %s seconds: stderr: %r stdout: %r1dhcpcd exited with code: %s stderr: %r stdout: %r)rrrrXrrrr<r rSrrorr r-Z get_proc_pgidr)killpgrrProcessLookupErrorrrrrrrrrr)r?r"r!r rrZinfiniband_argumentZcommandrrrtrrgidrrrrrrrrhs                 zDhcpcd.dhcp_discovery)r5dhcp_option_numberrcCs<d}ttddd}|||D]\}}||kr|SqdS)aVget a specific option from a binary lease file This is required until upstream dhcpcd supports unknown option 245 upstream bug: https://github.com/NetworkConfiguration/dhcpcd/issues/282 @param data: Binary lease data @param number: Option number to return @return: the option (bytes) or None )r5indexcssXt||dkrT||}|d|}|d|d||}||fVd||}qdS)zoptions are variable length, and consist of the following format option number: 1 byte option length: 1 byte option data: variable length (see length field) rrAN)rl)r5rcodelengthoptionrrr iter_optionss   z>Dhcpcd.parse_unknown_options_from_packet..iter_optionsN)bytesr)r5rZINDEXrrrrrr!parse_unknown_options_from_packets  z(Dhcpcd.parse_unknown_options_from_packet) lease_dumpr"rc Cstd||zHtdd|dddD}|sTd}t||t||Wn6ty}ztd|t|WYd }~n d }~00||d <d d | D}d dd}| D]\}}||vr| |||<qt d|d}t |d} | r t| |d<|S)a`parse the output of dhcpcd --dump map names to the datastructure we create from dhclient example dhcpcd output: broadcast_address='192.168.15.255' dhcp_lease_time='3600' dhcp_message_type='5' dhcp_server_identifier='192.168.0.1' domain_name='us-east-2.compute.internal' domain_name_servers='192.168.0.2' host_name='ip-192-168-0-212' interface_mtu='9001' ip_address='192.168.0.212' network_number='192.168.0.0' routers='192.168.0.1' subnet_cidr='20' subnet_mask='255.255.240.0' z)Parsing dhcpcd lease for interface %s: %rcSs"g|]}d|vr|jdddqS)=rA)maxsplit)rn)rarrrr(sz-Dhcpcd.parse_dhcpcd_lease..'r8 z;No valid DHCP lease configuration found in dhcpcd lease: %rzError parsing dhcpcd lease: %rNr"cSsi|]\}}|dd|qS)r-)rp)rrvaluerrr =rz-Dhcpcd.parse_dhcpcd_lease..z fixed-addressr)z ip-addresszclassless-static-routesz/var/lib/dhcpcd/z.leaserh)rrr$rorprnrrrr3popr Zload_binary_filerrrr) rr"rtrrZname_mapsourceZ destinationZ dhcp_messagerwrrrparse_dhcpcd_lease s@     zDhcpcd.parse_dhcpcd_leaserQc Cshz |t|jdd|gj|WStjyb}z(td|j|j|jt |WYd}~n d}~00dS)zReturn a dict of dhcp options. @param interface: which interface to dump the lease from @raises: InvalidDHCPLeaseFileError on empty or unparsable leasefile content. z --dumpleaserrN) rr r<rrrrrrr)r?r"rrrrrSTs& zDhcpcd.get_newest_leaserTcCsD|}|r4ddt|ddd|dddDStd|gS)a classless static routes as returned from dhcpcd --dumplease and return a list containing tuple of strings. The tuple is composed of the network_address (including net length) and gateway for a parsed static route. @param routes: string containing classless static routes @returns: list of tuple(str, str) for all valid parsed routes until the first parsing error. e.g.: sr=parse_static_routes( "0.0.0.0/0 10.0.0.1 168.63.129.16/32 10.0.0.1" ) sr=[ ("0.0.0.0/0", "10.0.0.1"), ("169.63.129.16/32", "10.0.0.1"), ] cSsg|]}|qSrrrirrrrrz.Dhcpcd.parse_static_routes..NrrAz'Malformed classless static routes: [%s])rnziprrrUrrrrrVqs ( zDhcpcd.parse_static_routes)NN)rrrr<rXrZr rrrrr]rrrrrSrr rVrrrrrds$ w+HrcsxeZdZdZfddZdeeeeee fdddZ eeee fdd d Z e ee eeefd d d ZZS)UdhcpcZudhcpccstd|_dS)Nr8r_r>rbrrr@s zUdhcpc.__init__NrWc CsVtd|tjdd}tj||d|_tt t |jWdn1sV0Y|j |tj|d}t |td|jdd d |d |d d ddg }t|r|ddt|ddddgztj|d|jidd\}}WnDtjy6} z(td| j| j| jt| WYd} ~ n d} ~ 00|durL|||||S)arRun udhcpc on the interface without scripts or filesystem artifacts. @param interface: Name of the network interface on which to run udhcpc. @param dhcp_log_func: A callable accepting the udhcpc output and error streams. @return: A list of dicts of representing the dhcp leases parsed from the udhcpc lease file. rTrz .lease.jsonN udhcpc_scriptiz-OZ staticroutesz-iz-sz-nz-qz-fz-vz-xz 0x3d:20{}rr{r8Z LEASE_FILE)Z update_envZcapturez1udhcpc exited with code: %s stderr: %r stdout: %r)rrr rr)r*r.rarrrFrrr r UDHCPC_SCRIPTr<rextendformatrrpr rrrrrrS) r?r"r!r rrcmdrrrrrrrsV   *    zUdhcpc.dhcp_discoveryrQcCstt|jS)aGet the most recent lease from the ephemeral phase as a dict. Return a dict of dhcp options. The dict contains key value pairs from the most recent lease. @param interface: an interface name - not used in this class, but required for function signature compatibility with other classes that require a distro object @raises: InvalidDHCPLeaseFileError on empty or unparsable leasefile content. )r Z load_jsonr-rarRrrrrSs zUdhcpc.get_newest_leaserTcCs8|}|r4ddt|ddd|dddDSgS)NcSsg|]}|qSrrrrrrrrz.Udhcpc.parse_static_routes..rrA)rnrrrrrrVs(zUdhcpc.parse_static_routes)NN)rrrr<r@rZr rrrrrSr]rr rVrrrrbrrs  Hr)NN)N)N)5r[rEZloggingr)rirrr~r contextlibrior subprocessrtypingrrrrr r r%Z cloudinitr r r Z cloudinit.netrrZ getLoggerrrr(rrr ExceptionrrrrrZr#r'r1r6ABCr7r^rrZALL_DHCP_CLIENTSrrrrsR     !   K.f