<commit_before>"""aiobfd: BFD Session with an individual remote"""
# pylint: disable=I0011,R0902,R0913
# pylint: disable=I0011,E1101
# socket.IPPROTO_IPV6 missing on Windows

import asyncio
import random
import socket
import time
import logging
import bitstring
from .transport import Client
from .packet import PACKET_FORMAT, PACKET_DEBUG_MSG
log = logging.getLogger(__name__)  # pylint: disable=I0011,C0103

SOURCE_PORT_MIN = 49152
SOURCE_PORT_MAX = 65535
CONTROL_PORT = 3784

VERSION = 1

DIAG_NONE = 0                       # No Diagnostic
DIAG_CONTROL_DETECTION_EXPIRED = 1  # Control Detection Time Expired
DIAG_ECHO_FAILED = 2                # Echo Function Failed
DIAG_NEIGHBOR_SIGNAL_DOWN = 3       # Neighbor Signaled Session Down
DIAG_FORWARD_PLANE_RESET = 4        # Forwarding Plane Reset
DIAG_PATH_DOWN = 5                  # Path Down
DIAG_CONCAT_PATH_DOWN = 6           # Concatenated Path Down
DIAG_ADMIN_DOWN = 7                 # Administratively Down
DIAG_REV_CONCAT_PATH_DOWN = 8       # Reverse Concatenated Path Down

STATE_ADMIN_DOWN = 0                # AdminDown
STATE_DOWN = 1                      # Down
STATE_INIT = 2                      # Init
STATE_UP = 3                        # Up

CONTROL_PLANE_INDEPENDENT = False   # Control Plane Independent

# Default timers
DESIRED_MIN_TX_INTERVAL = 1000000   # Minimum initial value

# Keep these fields statically disabled as they're not implemented
AUTH_TYPE = None                    # Authentication disabled
DEMAND_MODE = False                 # Demand Mode
MULTIPOINT = False                  # Multipoint
REQUIRED_MIN_ECHO_RX_INTERVAL = 0   # Do not support echo packet


class Session:
    """BFD session with a remote"""

    def __init__(self, local, remote, family=socket.AF_UNSPEC, passive=False,
                 tx_interval=1000000, rx_interval=1000000, detect_mult=3):
        # Argument variables
        self.local = local
        self.remote = remote
        self.family = family
        self.passive = passive
        self.loop = asyncio.get_event_loop()
        self.rx_interval = rx_interval  # User selectable value
        self.tx_interval = tx_interval  # User selectable value

        # As per 6.8.1. State Variables
        self.state = STATE_DOWN
        self.remote_state = STATE_DOWN
        self.local_discr = random.randint(0, 4294967295)  # 32-bit value
        self.remote_discr = 0
        self.local_diag = DIAG_NONE
        self._desired_min_tx_interval = DESIRED_MIN_TX_INTERVAL
        self._required_min_rx_interval = rx_interval
        self._remote_min_rx_interval = 1
        self.demand_mode = DEMAND_MODE
        self.remote_demand_mode = False
        self.detect_mult = detect_mult
        self.auth_type = AUTH_TYPE
        self.rcv_auth_seq = 0
        self.xmit_auth_seq = random.randint(0, 4294967295)  # 32-bit value
        self.auth_seq_known = False

        # State Variables beyond those defined in RFC 5880
        self._async_tx_interval = 1000000
        self._final_async_tx_interval = None  # Used to delay timer changes
        self.last_rx_packet_time = None
        self._async_detect_time = None
        self._final_async_detect_time = None  # Used to delay timer changes
        self.poll_sequence = False
        self._remote_detect_mult = None
        self._remote_min_tx_interval = None
        self._tx_packets = None

        # Create the local client and run it once to grab a port
        log.debug('Setting up UDP client for %s:%s.', remote, CONTROL_PORT)
        src_port = random.randint(SOURCE_PORT_MIN, SOURCE_PORT_MAX)
        fam, _, _, _, addr = socket.getaddrinfo(self.local, src_port)[0]
        sock = socket.socket(family=fam, type=socket.SOCK_DGRAM)
        if fam == socket.AF_INET:
            sock.setsockopt(socket.SOL_IP, socket.IP_TTL, 255)
        elif fam == socket.AF_INET6:
            # Under Windows the IPv6 socket constant is somehow missing
            # https://bugs.python.org/issue29515
            sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_UNICAST_HOPS, 255)
        sock.bind(addr)
        future = self.loop.create_datagram_endpoint(Client, sock=sock)
        self.client, _ = self.loop.run_until_complete(future)
        log.info('Sourcing traffic for %s:%s from %s:%s.',
                 remote, CONTROL_PORT,
                 self.client.get_extra_info('sockname')[0],
                 self.client.get_extra_info('sockname')[1])

        # Schedule the coroutines to transmit packets and detect failures
        self._tx_packets = asyncio.ensure_future(self.async_tx_packets())
        asyncio.ensure_future(self.detect_async_failure())

    # The transmit interval MUST be recalculated whenever
    # bfd.DesiredMinTxInterval changes, or whenever bfd.RemoteMinRxInterval
    # changes, and is equal to the greater of those two values.
    # If either bfd.DesiredMinTxInterval is changed or
    # bfd.RequiredMinRxInterval is changed, a Poll Sequence MUST be
    # initiated (see section 6.5)
    @property
    def desired_min_tx_interval(self):
        """bfd.DesiredMinTxInterval"""
        return self._desired_min_tx_interval

    @desired_min_tx_interval.setter
    def desired_min_tx_interval(self, value):
        if value == self._desired_min_tx_interval:
            return
        log.info('bfd.DesiredMinTxInterval changed from %d to %d, starting '
                 'Poll Sequence.', self._desired_min_tx_interval, value)

        # If bfd.DesiredMinTxInterval is increased and bfd.SessionState is Up,
        # the actual transmission interval used MUST NOT change until the Poll
        # Sequence described above has terminated.
        tx_interval = max(value, self.remote_min_rx_interval)
        if value > self._desired_min_tx_interval and self.state == STATE_UP:
            self._final_async_tx_interval = tx_interval
            log.info('Delaying increase in Tx Interval from %d to %d ...',
                     self._async_tx_interval, self._final_async_tx_interval)
        else:
            self._async_tx_interval = tx_interval
        self._desired_min_tx_interval = value
        self.poll_sequence = True

    @property
    def required_min_rx_interval(self):
        """bfd.RequiredMinRxInterval"""
        return self._required_min_rx_interval

    @required_min_rx_interval.setter
    def required_min_rx_interval(self, value):
        if value == self._required_min_rx_interval:
            return
        log.info('bfd.RequiredMinRxInterval changed from %d to %d, starting '
                 'Poll Sequence.', self._required_min_rx_interval, value)

        detect_time = self.calc_detect_time(self.remote_detect_mult,
                                            self.required_min_rx_interval,
                                            self.remote_min_tx_interval)
        if value < self._required_min_rx_interval and self.state == STATE_UP:
            self._final_async_detect_time = detect_time
            log.info('Delaying increase in Detect Time from %d to %d ...',
                     self._async_detect_time, self._final_async_detect_time)
        else:
            self._async_detect_time = detect_time
        self._required_min_rx_interval = value
        self.poll_sequence = True

    @property
    def remote_min_rx_interval(self):
        """Property for remote_min_rx_interval so we can re-calculate
            the async_tx_interval whenever this value changes"""
        return self._remote_min_rx_interval

    @remote_min_rx_interval.setter
    def remote_min_rx_interval(self, value):
        if value == self._remote_min_rx_interval:
            return
        # If the local system reduces its transmit interval due to
        # bfd.RemoteMinRxInterval being reduced (the remote system has
        # advertised a reduced value in Required Min RX Interval), and the
        # remote system is not in Demand mode, the local system MUST honor
        # the new interval immediately.
        # We should cancel the tx_packets coro to do this.
        tx_interval = max(value, self.desired_min_tx_interval)
        if tx_interval < self._async_tx_interval:<commit_msg>Use new value in detect_time calculation<commit_after>"""aiobfd: BFD Session with an individual remote"""
# pylint: disable=I0011,R0902,R0913
# pylint: disable=I0011,E1101
# socket.IPPROTO_IPV6 missing on Windows

import asyncio
import random
import socket
import time
import logging
import bitstring
from .transport import Client
from .packet import PACKET_FORMAT, PACKET_DEBUG_MSG
log = logging.getLogger(__name__)  # pylint: disable=I0011,C0103

SOURCE_PORT_MIN = 49152
SOURCE_PORT_MAX = 65535
CONTROL_PORT = 3784

VERSION = 1

DIAG_NONE = 0                       # No Diagnostic
DIAG_CONTROL_DETECTION_EXPIRED = 1  # Control Detection Time Expired
DIAG_ECHO_FAILED = 2                # Echo Function Failed
DIAG_NEIGHBOR_SIGNAL_DOWN = 3       # Neighbor Signaled Session Down
DIAG_FORWARD_PLANE_RESET = 4        # Forwarding Plane Reset
DIAG_PATH_DOWN = 5                  # Path Down
DIAG_CONCAT_PATH_DOWN = 6           # Concatenated Path Down
DIAG_ADMIN_DOWN = 7                 # Administratively Down
DIAG_REV_CONCAT_PATH_DOWN = 8       # Reverse Concatenated Path Down

STATE_ADMIN_DOWN = 0                # AdminDown
STATE_DOWN = 1                      # Down
STATE_INIT = 2                      # Init
STATE_UP = 3                        # Up

CONTROL_PLANE_INDEPENDENT = False   # Control Plane Independent

# Default timers
DESIRED_MIN_TX_INTERVAL = 1000000   # Minimum initial value

# Keep these fields statically disabled as they're not implemented
AUTH_TYPE = None                    # Authentication disabled
DEMAND_MODE = False                 # Demand Mode
MULTIPOINT = False                  # Multipoint
REQUIRED_MIN_ECHO_RX_INTERVAL = 0   # Do not support echo packet


class Session:
    """BFD session with a remote"""

    def __init__(self, local, remote, family=socket.AF_UNSPEC, passive=False,
                 tx_interval=1000000, rx_interval=1000000, detect_mult=3):
        # Argument variables
        self.local = local
        self.remote = remote
        self.family = family
        self.passive = passive
        self.loop = asyncio.get_event_loop()
        self.rx_interval = rx_interval  # User selectable value
        self.tx_interval = tx_interval  # User selectable value

        # As per 6.8.1. State Variables
        self.state = STATE_DOWN
        self.remote_state = STATE_DOWN
        self.local_discr = random.randint(0, 4294967295)  # 32-bit value
        self.remote_discr = 0
        self.local_diag = DIAG_NONE
        self._desired_min_tx_interval = DESIRED_MIN_TX_INTERVAL
        self._required_min_rx_interval = rx_interval
        self._remote_min_rx_interval = 1
        self.demand_mode = DEMAND_MODE
        self.remote_demand_mode = False
        self.detect_mult = detect_mult
        self.auth_type = AUTH_TYPE
        self.rcv_auth_seq = 0
        self.xmit_auth_seq = random.randint(0, 4294967295)  # 32-bit value
        self.auth_seq_known = False

        # State Variables beyond those defined in RFC 5880
        self._async_tx_interval = 1000000
        self._final_async_tx_interval = None  # Used to delay timer changes
        self.last_rx_packet_time = None
        self._async_detect_time = None
        self._final_async_detect_time = None  # Used to delay timer changes
        self.poll_sequence = False
        self._remote_detect_mult = None
        self._remote_min_tx_interval = None
        self._tx_packets = None

        # Create the local client and run it once to grab a port
        log.debug('Setting up UDP client for %s:%s.', remote, CONTROL_PORT)
        src_port = random.randint(SOURCE_PORT_MIN, SOURCE_PORT_MAX)
        fam, _, _, _, addr = socket.getaddrinfo(self.local, src_port)[0]
        sock = socket.socket(family=fam, type=socket.SOCK_DGRAM)
        if fam == socket.AF_INET:
            sock.setsockopt(socket.SOL_IP, socket.IP_TTL, 255)
        elif fam == socket.AF_INET6:
            # Under Windows the IPv6 socket constant is somehow missing
            # https://bugs.python.org/issue29515
            sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_UNICAST_HOPS, 255)
        sock.bind(addr)
        future = self.loop.create_datagram_endpoint(Client, sock=sock)
        self.client, _ = self.loop.run_until_complete(future)
        log.info('Sourcing traffic for %s:%s from %s:%s.',
                 remote, CONTROL_PORT,
                 self.client.get_extra_info('sockname')[0],
                 self.client.get_extra_info('sockname')[1])

        # Schedule the coroutines to transmit packets and detect failures
        self._tx_packets = asyncio.ensure_future(self.async_tx_packets())
        asyncio.ensure_future(self.detect_async_failure())

    # The transmit interval MUST be recalculated whenever
    # bfd.DesiredMinTxInterval changes, or whenever bfd.RemoteMinRxInterval
    # changes, and is equal to the greater of those two values.
    # If either bfd.DesiredMinTxInterval is changed or
    # bfd.RequiredMinRxInterval is changed, a Poll Sequence MUST be
    # initiated (see section 6.5)
    @property
    def desired_min_tx_interval(self):
        """bfd.DesiredMinTxInterval"""
        return self._desired_min_tx_interval

    @desired_min_tx_interval.setter
    def desired_min_tx_interval(self, value):
        if value == self._desired_min_tx_interval:
            return
        log.info('bfd.DesiredMinTxInterval changed from %d to %d, starting '
                 'Poll Sequence.', self._desired_min_tx_interval, value)

        # If bfd.DesiredMinTxInterval is increased and bfd.SessionState is Up,
        # the actual transmission interval used MUST NOT change until the Poll
        # Sequence described above has terminated.
        tx_interval = max(value, self.remote_min_rx_interval)
        if value > self._desired_min_tx_interval and self.state == STATE_UP:
            self._final_async_tx_interval = tx_interval
            log.info('Delaying increase in Tx Interval from %d to %d ...',
                     self._async_tx_interval, self._final_async_tx_interval)
        else:
            self._async_tx_interval = tx_interval
        self._desired_min_tx_interval = value
        self.poll_sequence = True

    @property
    def required_min_rx_interval(self):
        """bfd.RequiredMinRxInterval"""
        return self._required_min_rx_interval

    @required_min_rx_interval.setter
    def required_min_rx_interval(self, value):
        if value == self._required_min_rx_interval:
            return
        log.info('bfd.RequiredMinRxInterval changed from %d to %d, starting '
                 'Poll Sequence.', self._required_min_rx_interval, value)

        detect_time = self.calc_detect_time(self.remote_detect_mult,
                                            value,
                                            self.remote_min_tx_interval)
        if value < self._required_min_rx_interval and self.state == STATE_UP:
            self._final_async_detect_time = detect_time
            log.info('Delaying increase in Detect Time from %d to %d ...',
                     self._async_detect_time, self._final_async_detect_time)
        else:
            self._async_detect_time = detect_time
        self._required_min_rx_interval = value
        self.poll_sequence = True

    @property
    def remote_min_rx_interval(self):
        """Property for remote_min_rx_interval so we can re-calculate
            the async_tx_interval whenever this value changes"""
        return self._remote_min_rx_interval

    @remote_min_rx_interval.setter
    def remote_min_rx_interval(self, value):
        if value == self._remote_min_rx_interval:
            return
        # If the local system reduces its transmit interval due to
        # bfd.RemoteMinRxInterval being reduced (the remote system has
        # advertised a reduced value in Required Min RX Interval), and the
        # remote system is not in Demand mode, the local system MUST honor
        # the new interval immediately.
        # We should cancel the tx_packets coro to do this.
        tx_interval = max(value, self.desired_min_tx_interval)
        if tx_interval < self._async_tx_interval:
{'fraction_non_alphanumeric': 0.06532848962502309, 'fraction_numerical': 0.017609753094021303, 'mean_word_length': 2.5704550450648496, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 5, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1020780', 'n_tokens_mistral': 4978, 'n_tokens_neox': 4607, 'n_words': 1200}