<commit_before>"""BGP implementation for FAUCET."""

# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
# Copyright (C) 2015 <NAME>, <NAME> and <NAME>.
# Copyright (C) 2015 Research and Education Advanced Network New Zealand Ltd.
# Copyright (C) 2015--2017 The Contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import collections
import json
import ipaddress

from ryu.services.protocols.bgp.bgpspeaker import BGPSpeaker
from ryu.services.protocols.bgp.api.base import CoreNotStarted

from faucet.valve_util import btos


class FaucetBgp(object):

    def __init__(self, logger, metrics, send_flow_msgs):
        self.logger = logger
        self.metrics = metrics
        self._send_flow_msgs = send_flow_msgs
        self._dp_bgp_speakers = None
        self._valves = None

    def _bgp_route_handler(self, path_change, dp_id, vlan_vid):
        """Handle a BGP change event.

        Args:
            path_change (ryu.services.protocols.bgp.bgpspeaker.EventPrefix): path change
            dp_id (int): Datapath ID
            vlan_vid (int): VLAN VID
        """
        prefix = ipaddress.ip_network(btos(path_change.prefix))
        nexthop = ipaddress.ip_address(btos(path_change.nexthop))
        withdraw = path_change.is_withdraw
        flowmods = []
        if not self._valves:
            return
        valve = self._valves[dp_id]
        if vlan_vid not in valve.dp.vlans:
            return
        vlan = valve.dp.vlans[vlan_vid]
        if vlan.is_faucet_vip(nexthop):
            self.logger.error(
                'BGP nexthop %s for prefix %s cannot be us',
                nexthop, prefix)
            return
        if vlan.ip_in_vip_subnet(nexthop) is None:
            self.logger.error(
                'BGP nexthop %s for prefix %s is not a connected network',
                nexthop, prefix)
            return

        if withdraw:
            self.logger.info(
                'BGP withdraw %s nexthop %s', prefix, nexthop)
            flowmods = valve.del_route(vlan, prefix)
        else:
            self.logger.info(
                'BGP add %s nexthop %s', prefix, nexthop)
            flowmods = valve.add_route(vlan, nexthop, prefix)
        if flowmods:
            self._send_flow_msgs(vlan.dp_id, flowmods)

    def _create_bgp_speaker_for_vlan(self, vlan):
        """Set up BGP speaker for an individual VLAN if required.

        Args:
            vlan (vlan): VLAN associated with this speaker.
        Returns:
            ryu.services.protocols.bgp.bgpspeaker.BGPSpeaker: BGP speaker.
        """
        dp_id = vlan.dp_id
        vlan_vid = vlan.vid
        handler = lambda x: self._bgp_route_handler(x, dp_id, vlan_vid)
        bgp_speaker = BGPSpeaker(
            as_number=vlan.bgp_as,
            router_id=vlan.bgp_routerid,
            bgp_server_port=vlan.bgp_port,
            bgp_server_hosts=vlan.bgp_server_addresses,
            best_path_change_handler=handler)
        for faucet_vip in vlan.faucet_vips:
            bgp_speaker.prefix_add(
                prefix=str(faucet_vip), next_hop=str(faucet_vip.ip))
        for ipv in vlan.ipvs():
            routes = vlan.routes_by_ipv(ipv)
            for ip_dst, ip_gw in list(routes.items()):
                bgp_speaker.prefix_add(
                    prefix=str(ip_dst), next_hop=str(ip_gw))
        for bgp_neighbor_address in vlan.bgp_neighbor_addresses:
            bgp_speaker.neighbor_add(
                address=bgp_neighbor_address,
                remote_as=vlan.bgp_neighbor_as,
                local_address=vlan.bgp_local_address,
                enable_ipv4=True,
                enable_ipv6=True)
        return bgp_speaker

    def reset(self, valves):
        """Set up a BGP speaker for every VLAN that requires it."""
        # TODO: port status changes should cause us to withdraw a route.
        if self._dp_bgp_speakers:
            for dp_vlan_bgp_speakers in list(self._dp_bgp_speakers.values()):
                for bgp_speaker in list(dp_vlan_bgp_speakers.values()):
                    bgp_speaker.shutdown()
        self._valves = valves
        self._dp_bgp_speakers = collections.defaultdict(dict)
        for dp_id, valve in list(self._valves.items()):
            for vlan in list(valve.dp.vlans.values()):
                if vlan.bgp_as:
                    self._dp_bgp_speakers[dp_id][vlan.vid] = self._create_bgp_speaker_for_vlan(vlan)

    def update_metrics(self):
        """Update BGP metrics."""
        for dp_id, bgp_speakers in list(self._dp_bgp_speakers.items()):
            valve = self._valves[dp_id]
            for vlan_vid, bgp_speaker in list(bgp_speakers.items()):
                if bgp_speaker is None:
                    continue
                vlan = valve.dp.vlans[vlan_vid]
                try:
                    neighbor_states = list(json.loads(
                        bgp_speaker.neighbor_state_get()).items())
                except CoreNotStarted:
                    continue
                for neighbor, neighbor_state in neighbor_states:<commit_msg>Document Ryu only supports one BGP speaker. Determine BGP VLAN by looking up peer IP address.<commit_after>"""BGP implementation for FAUCET."""

# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
# Copyright (C) 2015 <NAME>, <NAME> and <NAME>.
# Copyright (C) 2015 Research and Education Advanced Network New Zealand Ltd.
# Copyright (C) 2015--2017 The Contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import collections
import json
import ipaddress

from ryu.services.protocols.bgp.bgpspeaker import BGPSpeaker
from ryu.services.protocols.bgp.api.base import CoreNotStarted

from faucet.valve_util import btos


class FaucetBgp(object):
    """Wrap Ryu BGP speaker implementation."""
    # TODO: Ryu BGP supports only one speaker
    # (https://sourceforge.net/p/ryu/mailman/message/32699012/)

    def __init__(self, logger, metrics, send_flow_msgs):
        self.logger = logger
        self.metrics = metrics
        self._send_flow_msgs = send_flow_msgs
        self._valves = None
        self._dp_bgp_speakers = None
        self._neighbor_to_vlan = {}

    def _bgp_route_handler(self, path_change):
        """Handle a BGP change event.

        Args:
            path_change (ryu.services.protocols.bgp.bgpspeaker.EventPrefix): path change
        """
        if not self._valves:
            return

        source = path_change.path.source.ip_address
        vlan = self._neighbor_to_vlan[source]
        if not source in self._neighbor_to_vlan:
            return

        vlan = self._neighbor_to_vlan[source]
        prefix = ipaddress.ip_network(btos(path_change.prefix))
        nexthop = ipaddress.ip_address(btos(path_change.nexthop))

        if vlan.is_faucet_vip(nexthop):
            self.logger.error(
                'BGP nexthop %s for prefix %s cannot be us',
                nexthop, prefix)
            return
        if vlan.ip_in_vip_subnet(nexthop) is None:
            self.logger.error(
                'BGP nexthop %s for prefix %s is not a connected network',
                nexthop, prefix)
            return

        valve = self._valves[vlan.dp_id]
        flowmods = []
        if path_change.is_withdraw:
            self.logger.info(
                'BGP withdraw %s nexthop %s', prefix, nexthop)
            flowmods = valve.del_route(vlan, prefix)
        else:
            self.logger.info(
                'BGP add %s nexthop %s', prefix, nexthop)
            flowmods = valve.add_route(vlan, nexthop, prefix)
        if flowmods:
            self._send_flow_msgs(vlan.dp_id, flowmods)

    def _create_bgp_speaker_for_vlan(self, vlan):
        """Set up BGP speaker for an individual VLAN if required.

        Args:
            vlan (vlan): VLAN associated with this speaker.
        Returns:
            ryu.services.protocols.bgp.bgpspeaker.BGPSpeaker: BGP speaker.
        """
        bgp_speaker = BGPSpeaker(
            as_number=0,
            router_id=vlan.bgp_routerid,
            bgp_server_port=vlan.bgp_port,
            bgp_server_hosts=vlan.bgp_server_addresses,
            best_path_change_handler=self._bgp_route_handler)
        for faucet_vip in vlan.faucet_vips:
            bgp_speaker.prefix_add(
                prefix=str(faucet_vip), next_hop=str(faucet_vip.ip))
        for ipv in vlan.ipvs():
            routes = vlan.routes_by_ipv(ipv)
            for ip_dst, ip_gw in list(routes.items()):
                bgp_speaker.prefix_add(
                    prefix=str(ip_dst), next_hop=str(ip_gw))
        for bgp_neighbor_address in vlan.bgp_neighbor_addresses:
            bgp_speaker.neighbor_add(
                address=bgp_neighbor_address,
                local_as=vlan.bgp_as,
                remote_as=vlan.bgp_neighbor_as,
                local_address=vlan.bgp_local_address,
                enable_ipv4=True,
                enable_ipv6=True)
            self._neighbor_to_vlan[bgp_neighbor_address] = vlan
        return bgp_speaker

    def reset(self, valves):
        """Set up a BGP speaker for every VLAN that requires it."""
        # TODO: port status changes should cause us to withdraw a route.
        if self._dp_bgp_speakers:
            for dp_vlan_bgp_speakers in list(self._dp_bgp_speakers.values()):
                for bgp_speaker in list(dp_vlan_bgp_speakers.values()):
                    bgp_speaker.shutdown()
        self._valves = valves
        self._dp_bgp_speakers = collections.defaultdict(dict)
        for dp_id, valve in list(self._valves.items()):
            for vlan in list(valve.dp.vlans.values()):
                if vlan.bgp_as:
                    self._dp_bgp_speakers[dp_id][vlan.vid] = self._create_bgp_speaker_for_vlan(vlan)

    def update_metrics(self):
        """Update BGP metrics."""
        for dp_id, bgp_speakers in list(self._dp_bgp_speakers.items()):
            valve = self._valves[dp_id]
            for vlan_vid, bgp_speaker in list(bgp_speakers.items()):
                if bgp_speaker is None:
                    continue
                vlan = valve.dp.vlans[vlan_vid]
                try:
                    neighbor_states = list(json.loads(
                        bgp_speaker.neighbor_state_get()).items())
                except CoreNotStarted:
                    continue
                for neighbor, neighbor_state in neighbor_states:
{'fraction_non_alphanumeric': 0.08115942028985507, 'fraction_numerical': 0.0053579270970575315, 'mean_word_length': 2.1947250280583614, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 1, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1315672', 'n_tokens_mistral': 3670, 'n_tokens_neox': 3523, 'n_words': 855}