o
    x[h›.  ã                   @   sB  d dl Z d dlZd dlZd dlZd dlZd dlmZ d dlmZ e  	e
¡ZdZdZdZdZdZd	ZdZd
ZdZdZe e¡Ze e¡Zee ZdZdZdZdZd ZdZdZ dZ!dZ"dZ#dZ$edg d¢ƒZ%edddgƒZ&edg d¢ƒZ'G dd„ de(ƒZ)dd„ Z*dd„ Z+d-dd „Z,d!d"„ Z-d#d$„ Z.d%d&„ Z/d'd(„ Z0d)d*„ Z1d+d,„ Z2dS ).é    N)Ú
namedtuple)Úutilé   é   é   é   é   iÿÿ  é<   ÚIHHIIÚBHiIIé   é   é   é   é   ÚRTAAttr)ÚlengthÚrta_typeÚdataÚInterfaceOperstateÚifnameÚ	operstateÚNetlinkHeader)r   ÚtypeÚflagsÚseqÚpidc                   @   s   e Zd ZdZdS )ÚNetlinkCreateSocketErrorz5Raised if netlink socket fails during create or bind.N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__© r"   r"   úC/usr/lib/python3/dist-packages/cloudinit/sources/helpers/netlink.pyr   5   s    r   c               
   C   sp   zt   t jt jt j¡} |  t ¡ tf¡ |  d¡ W n t j	y0 } z	d| }t
|ƒ|‚d}~ww t d¡ | S )au  Creates netlink socket and bind on netlink group to catch interface
    down/up events. The socket will bound only on RTMGRP_LINK (which only
    includes RTM_NEWLINK/RTM_DELLINK/RTM_GETLINK events). The socket is set to
    non-blocking mode since we're only receiving messages.

    :returns: netlink socket in non-blocking mode
    :raises: NetlinkCreateSocketError
    r   z*Exception during netlink socket create: %sNzCreated netlink socket)ÚsocketÚ
AF_NETLINKÚSOCK_RAWÚNETLINK_ROUTEÚbindÚosÚgetpidÚRTMGRP_LINKÚsetblockingÚerrorr   ÚLOGÚdebug)Únetlink_socketÚeÚmsgr"   r"   r#   Úcreate_bound_netlink_socket9   s   	ÿ
€þ
r3   c                 C   s^   | dusJ dƒ‚t | ƒtksJ dƒ‚t t| dt… ¡\}}}}}t d|¡ t|||||ƒS )a  Gets netlink message type and length

    :param: data read from netlink socket
    :returns: netlink message type
    :raises: AssertionError if data is None or data is not >= NLMSGHDR_SIZE
    struct nlmsghdr {
               __u32 nlmsg_len;    /* Length of message including header */
               __u16 nlmsg_type;   /* Type of message content */
               __u16 nlmsg_flags;  /* Additional flags */
               __u32 nlmsg_seq;    /* Sequence number */
               __u32 nlmsg_pid;    /* Sender port ID */
    };
    Núdata is nonez+data is smaller than netlink message headerzGot netlink msg of type %d)	ÚlenÚNLMSGHDR_SIZEÚstructÚunpackÚNLMSGHDR_FMTÚMSG_TYPE_OFFSETr.   r/   r   )r   Úmsg_lenÚmsg_typer   r   r   r"   r"   r#   Úget_netlink_msg_headerO   s   ÿÿr=   c                 C   s^   | dusJ dƒ‚t   | gg g |¡\}}}| |vrdS t d¡ |  t¡}|du r-t d¡ |S )aÁ  Select and read from the netlink socket if ready.

    :param: netlink_socket: specify which socket object to read from
    :param: timeout: specify a timeout value (integer) to wait while reading,
            if none, it will block indefinitely until socket ready for read
    :returns: string of data read (max length = <MAX_SIZE>) from socket,
              if no data read, returns None
    :raises: AssertionError if netlink_socket is None
    Núnetlink socket is noneznetlink socket ready for readz,Reading from Netlink socket returned no data)Úselectr.   r/   ÚrecvÚMAX_SIZEr-   )r0   ÚtimeoutÚread_setÚ_r   r"   r"   r#   Úread_netlink_socketh   s   



rE   c                 C   s¦   | dusJ dƒ‚t |tƒsJ dƒ‚|tksJ dƒ‚d }}d}ztjd| |d�d }tjd| |d d�d }W n tjyB   Y dS w | |t || … }t|||ƒS )	a(  Unpack a single rta attribute.

    :param: data: string of data read from netlink socket
    :param: offset: starting offset of RTA Attribute
    :return: RTAAttr object with length, type and data. On error, return None.
    :raises: AssertionError if data is None or offset is not integer.
    Nr4   zoffset is not integerz'rta offset is less than expected lengthr   ÚH)Úoffsetr   )Ú
isinstanceÚintÚRTATTR_START_OFFSETr7   Úunpack_fromr-   ÚRTA_DATA_START_OFFSETr   )r   rG   r   r   Ú	attr_datar"   r"   r#   Úunpack_rta_attr   s   
ÿÿrN   c                 C   sâ   | dusJ dƒ‚t | ƒtksJ dƒ‚d }}t}|t | ƒkr]t| |ƒ}|r*|jdkr+n2t|jt  t }||j| 7 }|jtkrFt|jƒ}n|jt	krWt
 |jd¡}| d¡}|t | ƒks|rc|du redS t d||¡ t||ƒS )a�  Reads Interface name and operational state from RTA Data.

    :param: data: string of data read from netlink socket
    :returns: InterfaceOperstate object containing if_name and oper_state.
              None if data does not contain valid IFLA_OPERSTATE and
              IFLA_IFNAME messages.
    :raises: AssertionError if data is None or length of data is
             smaller than RTATTR_START_OFFSET.
    Nr4   z2length of data is smaller than RTATTR_START_OFFSETr   zutf-8ú z!rta attrs: ifname %s operstate %d)r5   rJ   rN   r   ÚPAD_ALIGNMENTr   ÚIFLA_OPERSTATEÚordr   ÚIFLA_IFNAMEr   Údecode_binaryÚstripr.   r/   r   )r   r   r   rG   ÚattrÚpadlenÚinterface_namer"   r"   r#   Úread_rta_oper_state™   s0   
ÿ
þ


ò
rY   c                    s6   t  d¡ d‰‡ ‡fdd„}t| dtgttg|ƒ ˆS )zêBlock until a single nic is attached.

    :param: netlink_socket: netlink_socket to receive events
    :param: existing_nics: List of existing nics so that we can skip them.
    :raises: AssertionError if netlink_socket is none.
    z!Preparing to wait for nic attach.Nc                    s   | ˆ v rdS | ‰dS )NTFr"   ©ÚinameÚcarrierÚprevCarrier©Úexisting_nicsr   r"   r#   Úshould_continue_cbÈ   s   z5wait_for_nic_attach_event.<locals>.should_continue_cb)r.   r/   Úread_netlink_messagesÚRTM_NEWLINKÚOPER_UPÚ	OPER_DOWN)r0   r_   r`   r"   r^   r#   Úwait_for_nic_attach_event¾   s   
	ûre   c                    s2   t  d¡ d‰ ‡ fdd„}t| dtgtg|ƒ ˆ S )z‹Block until a single nic is detached and its operational state is down.

    :param: netlink_socket: netlink_socket to receive events.
    z!Preparing to wait for nic detach.Nc                    s   | ‰ dS )NFr"   rZ   ©r   r"   r#   r`   ã   s   z5wait_for_nic_detach_event.<locals>.should_continue_cb)r.   r/   ra   ÚRTM_DELLINKrd   )r0   r`   r"   rf   r#   Úwait_for_nic_detach_eventÛ   s   
ÿrh   c                    sf   | dusJ dƒ‚ˆ dusJ dƒ‚t ˆ ƒdksJ dƒ‚‡ fdd„}t d¡ t| ˆ ttgttg|ƒ dS )	a»  Block until media disconnect and connect has happened on an interface.
    Listens on netlink socket to receive netlink events and when the carrier
    changes from 0 to 1, it considers event has happened and
    return from this function

    :param: netlink_socket: netlink_socket to receive events
    :param: ifname: Interface name to lookout for netlink events
    :raises: AssertionError if netlink_socket is None or ifname is None.
    Nr>   zinterface name is noner   zinterface name cannot be emptyc                    s(   |t ko|tk}|rt dˆ ¡ dS dS )NzMedia switch happened on %s.FT)rd   rc   r.   r/   )r[   r\   r]   ÚisVnetSwitchrf   r"   r#   r`   ü   s
   z=wait_for_media_disconnect_connect.<locals>.should_continue_cbz1Wait for media disconnect and reconnect to happen)r5   r.   r/   ra   rb   rg   rc   rd   )r0   r   r`   r"   rf   r#   Ú!wait_for_media_disconnect_connectî   s   

ûrj   c                 C   sn  | du rt dƒ‚tƒ }t}t}	 t| tƒ}|du rqt dt|ƒ¡ ||7 }t dt|ƒ¡ d}	t|ƒ}
|	|
k r°||	d… }t|ƒtk rJt d¡ nft	|ƒ}t|ƒ|j
k r[t d¡ nU|j
t d	 td	  @ }|	| }	t d
|	¡ |j|vrwq4t|ƒ}|du r†t d|¡ q4|dur˜|j|kr˜t d|j|¡ q4|j|vržq4|}|j}||j||ƒs¬dS |	|
k s8||	d… }q)a°  Reads from the netlink socket until the condition specified by
    the continuation callback is met.

    :param: netlink_socket: netlink_socket to receive events.
    :param: ifname_filter: if not None, will only listen for this interface.
    :param: rtm_types: Type of netlink events to listen for.
    :param: operstates: Operational states to listen.
    :param: should_continue_callback: Specifies when to stop listening.
    NzNetlink socket is noneTzread %d bytes from socketzLength of data after concat %dr   z#Data is smaller than netlink headerz*Partial data. Smaller than netlink messager   z"offset to next netlink message: %dz!Failed to read rta attributes: %sz6Ignored netlink event on interface %s. Waiting for %s.)ÚRuntimeErrorÚbytesrc   rE   ÚSELECT_TIMEOUTr.   r/   r5   r6   r=   r   rP   r   rY   r   r   )r0   Úifname_filterÚ	rtm_typesÚ
operstatesÚshould_continue_callbackr   r\   r]   Ú	recv_datarG   ÚdatalenÚnl_msgÚnlheaderrW   Úinterface_stater"   r"   r#   ra     sj   


ÿ

ý
ÿÚ'Ðra   )N)3Úloggingr)   r?   r$   r7   Úcollectionsr   Ú	cloudinitr   Ú	getLoggerr   r.   r+   rb   rg   ÚRTM_GETLINKÚRTM_SETLINKrA   r:   rm   r9   ÚIFINFOMSG_FMTÚcalcsizer6   ÚIFINFOMSG_SIZErJ   rL   rP   rS   rQ   ÚOPER_UNKNOWNÚOPER_NOTPRESENTrd   ÚOPER_LOWERLAYERDOWNÚOPER_TESTINGÚOPER_DORMANTrc   r   r   r   rk   r   r3   r=   rE   rN   rY   re   rh   rj   ra   r"   r"   r"   r#   Ú<module>   s^   


ÿ
% 