o
    w7e/                     @   sV  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 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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  Z0d/d!d"Z1d#d$ Z2d%d& Z3d'd( Z4d)d* Z5d+d, Z6d-d. Z7dS )0    N)
namedtuple)log)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   9   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_socket=   s   	

r5   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_OFFSETr0   r1   r   )r   msg_lenmsg_typer   r   r   r$   r$   r%   get_netlink_msg_headerS   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)selectr0   r1   recvMAX_SIZEr/   )r2   timeoutread_set_r   r$   r$   r%   read_netlink_socketl   s   



rG   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.
    Nr6   zoffset is not integerz'rta offset is less than expected lengthr   H)offsetr   )
isinstanceintRTATTR_START_OFFSETr9   unpack_fromr/   RTA_DATA_START_OFFSETr   )r   rI   r   r   	attr_datar$   r$   r%   unpack_rta_attr   s   
rP   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.
    Nr6   z2length of data is smaller than RTATTR_START_OFFSETr   zutf-8 z!rta attrs: ifname %s operstate %d)r7   rL   rP   r   PAD_ALIGNMENTr   IFLA_OPERSTATEordr   IFLA_IFNAMEr   decode_binarystripr0   r1   r   )r   r   r   rI   attrpadleninterface_namer$   r$   r%   read_rta_oper_state   s0   





r[   c                    s6   t d d fdd}t| dtgttg| S )zBlock 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)r0   r1   read_netlink_messagesRTM_NEWLINKOPER_UP	OPER_DOWN)r2   ra   rb   r$   r`   r%   wait_for_nic_attach_event   s   
	rg   c                    s2   t d d  fdd}t| dtgtg|  S )zBlock 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$   r\   r   r$   r%   rb      s   z5wait_for_nic_detach_event.<locals>.should_continue_cb)r0   r1   rc   RTM_DELLINKrf   )r2   rb   r$   rh   r%   wait_for_nic_detach_event   s   
rj   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)rf   re   r0   r1   )r]   r^   r_   isVnetSwitchrh   r$   r%   rb      s
   z=wait_for_media_disconnect_connect.<locals>.should_continue_cbz1Wait for media disconnect and reconnect to happen)r7   r0   r1   rc   rd   ri   re   rf   )r2   r   rb   r$   rh   r%   !wait_for_media_disconnect_connect   s   

rl   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 rtd| q4|dur|j|krtd|j| q4|j|vrq4|}|j}||j||sd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bytesre   rG   SELECT_TIMEOUTr0   r1   r7   r8   r?   r   rR   r   r[   r   r   )r2   ifname_filter	rtm_types
operstatesshould_continue_callbackr   r^   r_   	recv_datarI   datalennl_msgnlheaderrY   interface_stater$   r$   r%   rc     sj   





'rc   )N)8r+   rA   r&   r9   collectionsr   	cloudinitr   loggingr   	getLoggerr    r0   r-   
NLMSG_NOOPNLMSG_ERROR
NLMSG_DONErd   ri   RTM_GETLINKRTM_SETLINKrC   RTA_DATA_OFFSETr<   ro   r;   IFINFOMSG_FMTcalcsizer8   IFINFOMSG_SIZErL   rN   rR   rU   rS   OPER_UNKNOWNOPER_NOTPRESENTrf   OPER_LOWERLAYERDOWNOPER_TESTINGOPER_DORMANTre   r   r   r   rm   r   r5   r?   rG   rP   r[   rg   rj   rl   rc   r$   r$   r$   r%   <module>   sf   



% 