/* *------------------------------------------------------------------ * Copyright (c) 2017 Cisco and/or its affiliates. * 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. *------------------------------------------------------------------ */ #include #include #include #include #include #include void bond_disable_collecting_distributing (vlib_main_t * vm, member_if_t * mif) { bond_main_t *bm = &bond_main; bond_if_t *bif; int i; uword p; u8 switching_active = 0; bif = bond_get_bond_if_by_dev_instance (mif->bif_dev_instance); clib_spinlock_lock_if_init (&bif->lockp); vec_foreach_index (i, bif->active_members) { p = *vec_elt_at_index (bif->active_members, i); if (p == mif->sw_if_index) { if ((bif->mode == BOND_MODE_ACTIVE_BACKUP) && (i == 0) && (vec_len (bif->active_members) > 1)) /* deleting the active member for active-backup */ switching_active = 1; vec_del1 (bif->active_members, i); if (mif->lacp_enabled && bif->numa_only) { /* For lacp mode, if we check it is a member on local numa node, bif->n_numa_members should be decreased by 1 becasue the first bif->n_numa_members are all members on local numa node */ if (i < bif->n_numa_members) { bif->n_numa_members--; ASSERT (bif->n_numa_members >= 0); } } break; } } /* We get a new member just becoming active */ if (switching_active) vlib_process_signal_event (bm->vlib_main, bond_process_node.index, BOND_SEND_GARP_NA, bif->hw_if_index); clib_spinlock_unlock_if_init (&bif->lockp); } /* * return 1 if s2 is preferred. * return -1 if s1 is preferred. */ static int bond_member_sort (void *a1, void *a2) { u32 *s1 = a1; u32 *s2 = a2; member_if_t *mif1 = bond_get_member_by_sw_if_index (*s1); member_if_t *mif2 = bond_get_member_by_sw_if_index (*s2); bond_if_t *bif; ALWAYS_ASSERT (mif1); ALWAYS_ASSERT (mif2); /* * sort entries according to preference rules: * 1. biggest weight * 2. numa-node * 3. current active member (to prevent churning) * 4. lowest sw_if_index (for deterministic behavior) * */ if (mif2->weight > mif1->weight) return 1; if (mif2->weight < mif1->weight) return -1; else { if (mif2->is_local_numa > mif1->is_local_numa) return 1; if (mif2->is_local_numa < mif1->is_local_numa) return -1; else { bif = bond_get_bond_if_by_dev_instance (mif1->bif_dev_instance); /* Favor the current active member to avoid churning */ if (bif->active_members[0] == mif2->sw_if_index) return 1; if (bif->active_members[0] == mif1->sw_if_index) return -1; /* go for the tiebreaker as the last resort */ if (mif1->sw_if_index > mif2->sw_if_index) return 1; if (mif1->sw_if_index < mif2->sw_if_index) return -1; else ASSERT (0); } } return 0; } static void bond_sort_members (bond_if_t * bif) { bond_main_t *bm = &bond_main; u32 old_active = bif->active_members[0]; vec_sort_with_function (bif->active_members, bond_member_sort); if (old_active != bif->active_members[0]) vlib_process_signal_event (bm->vlib_main, bond_process_node.index, BOND_SEND_GARP_NA, bif->hw_if_index); } void bond_enable_collecting_distributing (vlib_main_t * vm, member_if_t * mif) { bond_if_t *bif; bond_main_t *bm = &bond_main; vnet_main_t *vnm = vnet_get_main (); vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, mif->sw_if_index); int i; uword p; bif = bond_get_bond_if_by_dev_instance (mif->bif_dev_instance); clib_spinlock_lock_if_init (&bif->lockp); vec_foreach_index (i, bif->active_members) { p = *vec_elt_at_index (bif->active_members, i); if (p == mif->sw_if_index) goto done; } if (mif->lacp_enabled && bif->numa_only && (vm->numa_node == hw->numa_node)) { vec_insert_elts (bif->active_members, &mif->sw_if_index, 1, bif->n_numa_members); bif->n_numa_members++; } else vec_add1 (bif->active_members, mif->sw_if_index); mif->is_local_numa = (vm->numa_node == hw->numa_node) ? 1 : 0; if (bif->mode == BOND_MODE_ACTIVE_BACKUP) { if (vec_len (bif->active_members) == 1) /* First member becomes active? */ vlib_process_signal_event (bm->vlib_main, bond_process_node.index, BOND_SEND_GARP_NA, bif->hw_if_index); else bond_sort_members (bif); } done: clib_spinlock_unlock_if_init (&bif->lockp); } int bond_dump_ifs (bond_interface_details_t ** out_bondifs) { vnet_main_t *vnm = vnet_get_main (); bond_main_t *bm = &bond_main; bond_if_t *bif; vnet_hw_interface_t *hi; bond_interface_details_t *r_bondifs = NULL; bond_interface_details_t *bondif = NULL; /* *INDENT-OFF* */ pool_foreach (bif, bm->interfaces) { vec_add2(r_bondifs, bondif, 1); clib_memset (bondif, 0, sizeof (*bondif)); bondif->id = bif->id; bondif->sw_if_index = bif->sw_if_index; hi = vnet_get_hw_interface (vnm, bif->hw_if_index); clib_memcpy(bondif->interface_name, hi->name, MIN (ARRAY_LEN (bondif->interface_name) - 1, vec_len ((const char *) hi->name))); /* enforce by memset() above */ ASSERT(0 == bondif->interface_name[ARRAY_LEN (bondif->interface_name) - 1]); bondif->mode = bif->mode; bondif->lb = bif->lb; bondif->numa_only = bif->numa_only; bondif->active_members = vec_len (bif->active_members); bondif->members = vec_len (bif->members); } /* *INDENT-ON* */ *out_bondifs = r_bondifs; return 0; } int bond_dump_member_ifs (member_interface_details_t ** out_memberifs, u32 bond_sw_if_index) { vnet_main_t *vnm = vnet_get_main (); bond_if_t *bif; vnet_hw_interface_t *hi; vnet_sw_interface_t *sw; member_interface_details_t *r_memberifs = NULL; member_interface_details_t *memberif = NULL; u32 *sw_if_index = NULL; member_if_t *mif; bif = bond_get_bond_if_by_sw_if_index (bond_sw_if_index); if (!bif) return 1; vec_foreach (sw_if_index, bif->members) { vec_add2 (r_memberifs, memberif, 1); clib_memset (memberif, 0, sizeof (*memberif)); mif = bond_get_member_by_sw_if_index (*sw_if_index); if (mif) { sw = vnet_get_sw_interface (vnm, mif->sw_if_index); hi = vnet_get_hw_interface (vnm, sw->hw_if_index); clib_memcpy (memberif->interface_name, hi->name, MIN (ARRAY_LEN (memberif->interface_name) - 1, vec_len ((const char *) hi->name))); /* enforce by memset() above */ ASSERT (0 == memberif->interface_name[ARRAY_LEN (memberif->interface_name) - 1]); memberif->sw_if_index = mif->sw_if_index; memberif->is_passive = mif->is_passive; memberif->is_long_timeout = mif->is_long_timeout; memberif->is_local_numa = mif->is_local_numa; memberif->weight = mif->weight; } } *out_memberifs = r_memberifs; return 0; } /* * Manage secondary mac addresses when attaching/detaching a member. * If adding, copy any secondary addresses from bond interface to member. * If deleting, delete the bond interface's secondary addresses from the * member. */ static void bond_member_add_del_mac_addrs (bond_if_t * bif, u32 mif_sw_if_index, u8 is_add) { vnet_main_t *vnm = vnet_get_main (); ethernet_interface_t *b_ei; ethernet_interface_address_t *sec_mac; vnet_hw_interface_t *s_hwif; b_ei = ethernet_get_interface (ðernet_main, bif->hw_if_index); if (!b_ei || !b_ei->secondary_addrs) return; s_hwif = vnet_get_sup_hw_interface (vnm, mif_sw_if_index); vec_foreach (sec_mac, b_ei->secondary_addrs) vnet_hw_interface_add_del_mac_address (vnm, s_hwif->hw_if_index, sec_mac->mac.bytes, is_add); } static void bond_delete_neighbor (vlib_main_t * vm, bond_if_t * bif, member_if_t * mif) { bond_main_t *bm = &bond_main; vnet_main_t *vnm = vnet_get_main (); int i; vnet_hw_interface_t *mif_hw; mif_hw = vnet_get_sup_hw_interface (vnm, mif->sw_if_index); bif->port_number_bitmap = clib_bitmap_set (bif->port_number_bitmap, ntohs (mif->actor_admin.port_number) - 1, 0); bm->member_by_sw_if_index[mif->sw_if_index] = 0; vec_free (mif->last_marker_pkt); vec_free (mif->last_rx_pkt); vec_foreach_index (i, bif->members) { uword p = *vec_elt_at_index (bif->members, i); if (p == mif->sw_if_index) { vec_del1 (bif->members, i); break; } } bond_disable_collecting_distributing (vm, mif); vnet_feature_enable_disable ("device-input", "bond-input", mif->sw_if_index, 0, 0, 0); /* Put back the old mac */ vnet_hw_interface_change_mac_address (vnm, mif_hw->hw_if_index, mif->persistent_hw_address); /* delete the bond's secondary/virtual mac addrs from the member */ bond_member_add_del_mac_addrs (bif, mif->sw_if_index, 0 /* is_add */ ); if ((bif->mode == BOND_MODE_LACP) && bm->lacp_enable_disable) (*bm->lacp_enable_disable) (vm, bif, mif, 0); if (bif->mode == BOND_MODE_LACP) { vlib_stats_remove_entry ( bm->stats[bif->sw_if_index][mif->sw_if_index].actor_state); vlib_stats_remove_entry ( bm->stats[bif->sw_if_index][mif->sw_if_index].partner_state); } pool_put (bm->neighbors, mif); } int bond_delete_if (vlib_main_t * vm, u32 sw_if_index) { bond_main_t *bm = &bond_main; vnet_main_t *vnm = vnet_get_main (); bond_if_t *bif; member_if_t *mif; vnet_hw_interface_t *hw; u32 *mif_sw_if_index; u32 *s_list = 0; hw = vnet_get_sup_hw_interface (vnm, sw_if_index); if (hw == NULL || bond_dev_class.index != hw->dev_class_index) return VNET_API_ERROR_INVALID_SW_IF_INDEX; bif = bond_get_bond_if_by_dev_instance (hw->dev_instance); vec_append (s_list, bif->members); vec_foreach (mif_sw_if_index, s_list) { mif = bond_get_member_by_sw_if_index (*mif_sw_if_index); if (mif) bond_delete_neighbor (vm, bif, mif); } vec_free (s_list); /* bring down the interface */ vnet_hw_interface_set_flags (vnm, bif->hw_if_index, 0); vnet_sw_interface_set_flags (vnm, bif->sw_if_index, 0); ethernet_delete_interface (vnm, bif->hw_if_index); clib_bitmap_free (bif->port_number_bitmap); hash_unset (bm->bond_by_sw_if_index, bif->sw_if_index); hash_unset (bm->id_used, bif->id); clib_memset (bif, 0, sizeof (*bif)); pool_put (bm->interfaces, bif); return 0; } void bond_create_if (vlib_main_t * vm, bond_create_if_args_t * args) { vnet_eth_interface_registration_t eir = {}; bond_main_t *bm = &bond_main; vnet_main_t *vnm = vnet_get_main (); vnet_sw_interface_t *sw; bond_if_t *bif; if ((args->mode == BOND_MODE_LACP) && bm->lacp_plugin_loaded == 0) { args->rv = VNET_API_ERROR_FEATURE_DISABLED; args->error = clib_error_return (0, "LACP plugin is not loaded"); return; } if (args->mode > BOND_MODE_LACP || args->mode < BOND_MODE_ROUND_ROBIN) { args->rv = VNET_API_ERROR_INVALID_ARGUMENT; args->error = clib_error_return (0, "Invalid mode"); return; } if (args->lb > BOND_LB_L23) { args->rv = VNET_API_ERROR_INVALID_ARGUMENT; args->error = clib_error_return (0, "Invalid load-balance"); return; } pool_get (bm->interfaces, bif); clib_memset (bif, 0, sizeof (*bif)); bif->dev_instance = bif - bm->interfaces; bif->id = args->id; bif->lb = args->lb; bif->mode = args->mode; bif->gso = args->gso; if (bif->lb == BOND_LB_L2) bif->hash_func = vnet_hash_function_from_name ("hash-eth-l2", VNET_HASH_FN_TYPE_ETHERNET); else if (bif->lb == BOND_LB_L34) bif->hash_func = vnet_hash_function_from_name ("hash-eth-l34", VNET_HASH_FN_TYPE_ETHERNET); else if (bif->lb == BOND_LB_L23) bif->hash_func = vnet_hash_function_from_name ("hash-eth-l23", VNET_HASH_FN_TYPE_ETHERNET); // Adjust requested interface id if (bif->id == ~0) bif->id = bif->dev_instance; if (hash_get (bm->id_used, bif->id)) { args->rv = VNET_API_ERROR_INSTANCE_IN_USE; pool_put (bm->interfaces, bif); return; } hash_set (bm->id_used, bif->id, 1); // Special load-balance mode used for rr and bc if (bif->mode == BOND_MODE_ROUND_ROBIN) bif->lb = BOND_LB_RR; else if (bif->mode == BOND_MODE_BROADCAST) bif->lb = BOND_LB_BC; else if (bif->mode == BOND_MODE_ACTIVE_BACKUP) bif->lb = BOND_LB_AB; bif->use_custom_mac = args->hw_addr_set; if (!args->hw_addr_set) { f64 now = vlib_time_now (vm); u32 rnd; rnd = (u32) (now * 1e6); rnd = random_u32 (&rnd); memcpy (args->hw_addr + 2, &rnd, sizeof (rnd)); args->hw_addr[0] = 2; args->hw_addr[1] = 0xfe; } memcpy (bif->hw_address, args->hw_addr, 6); eir.dev_class_index = bond_dev_class.index; eir.dev_instance = bif->dev_instance
/*
 * Copyright (c) 2016 Cisco and/or its affiliates.
 * 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.
 */

#include <vnet/fib/fib_entry_delegate.h>
#include <vnet/fib/fib_entry.h>
#include <vnet/fib/fib_attached_export.h>

static fib_entry_delegate_t *
fib_entry_delegate_find_i (const fib_entry_t *fib_entry,
                           fib_entry_delegate_type_t type,
                           u32 *index)
{
    fib_entry_delegate_t *delegate;
    int ii;

    ii = 0;
    vec_foreach(delegate, fib_entry->fe_delegates)
    {
	if (delegate->fd_type == type)
	{
            if (NULL != index)
                *index = ii;

	    return (delegate);
	}
	else
	{
	    ii++;
	}
    }

    return (NULL);
}

fib_entry_delegate_t *
fib_entry_delegate_get (const fib_entry_t *fib_entry,
                        fib_entry_delegate_type_t type)
{
    return (fib_entry_delegate_find_i(fib_entry, type, NULL));
}

void
fib_entry_delegate_remove (fib_entry_t *fib_entry,
                           fib_entry_delegate_type_t type)
{
    fib_entry_delegate_t *fed;
    u32 index = ~0;

    fed = fib_entry_delegate_find_i(fib_entry, type, &index);

    ASSERT(NULL != fed);

    vec_del1(fib_entry->fe_delegates, index);
}

static int
fib_entry_delegate_cmp_for_sort (void * v1,
                                 void * v2)
{
    fib_entry_delegate_t *delegate1 = v1, *delegate2 = v2;

    return (delegate1->fd_type - delegate2->fd_type);
}

static void
fib_entry_delegate_init (fib_entry_t *fib_entry,
                         fib_entry_delegate_type_t type)

{
    fib_entry_delegate_t delegate = {
	.fd_entry_index = fib_entry_get_index(fib_entry),
	.fd_type = type,
    };

    vec_add1(fib_entry->fe_delegates, delegate);
    vec_sort_with_function(fib_entry->fe_delegates,
			   fib_entry_delegate_cmp_for_sort);
}

fib_entry_delegate_t *
fib_entry_delegate_find_or_add (fib_entry_t *fib_entry,
                                fib_entry_delegate_type_t fdt)
{
    fib_entry_delegate_t *delegate;

    delegate = fib_entry_delegate_get(fib_entry, fdt);

    if (NULL == delegate)
    {
	fib_entry_delegate_init(fib_entry, fdt);
    }

    return (fib_entry_delegate_get(fib_entry, fdt));
}

fib_entry_delegate_type_t
fib_entry_chain_type_to_delegate_type (fib_forward_chain_type_t fct)
{
    switch (fct)
    {
    case FIB_FORW_CHAIN_TYPE_UNICAST_IP4:
        return (FIB_ENTRY_DELEGATE_CHAIN_UNICAST_IP4);
    case FIB_FORW_CHAIN_TYPE_UNICAST_IP6: