/* *------------------------------------------------------------------ * 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. *------------------------------------------------------------------ */ #define _GNU_SOURCE #include #include #include #include #ifndef CLIB_MARCH_VARIANT bond_main_t bond_main; #endif /* CLIB_MARCH_VARIANT */ #define foreach_bond_input_error \ _(NONE, "no error") \ _(IF_DOWN, "interface down") \ _(PASSIVE_IF, "traffic received on passive interface") \ _(PASS_THRU, "pass through (CDP, LLDP, slow protocols)") typedef enum { #define _(f,s) BOND_INPUT_ERROR_##f, foreach_bond_input_error #undef _ BOND_INPUT_N_ERROR, } bond_input_error_t; static char *bond_input_error_strings[] = { #define _(n,s) s, foreach_bond_input_error #undef _ }; static u8 * format_bond_input_trace (u8 * s, va_list * args) { CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); bond_packet_trace_t *t = va_arg (*args, bond_packet_trace_t *); s = format (s, "src %U, dst %U, %U -> %U", format_ethernet_address, t->ethernet.src_address, format_ethernet_address, t->ethernet.dst_address, format_vnet_sw_if_index_name, vnet_get_main (), t->sw_if_index, format_vnet_sw_if_index_name, vnet_get_main (), t->bond_sw_if_index); return s; } typedef enum { BOND_INPUT_NEXT_DROP, BOND_INPUT_N_NEXT, } bond_output_next_t; static_always_inline u8 packet_is_cdp (ethernet_header_t * eth) { llc_header_t *llc; snap_header_t *snap; llc = (llc_header_t *) (eth + 1); snap = (snap_header_t *) (llc + 1); return ((eth->type == htons (ETHERNET_TYPE_CDP)) || ((llc->src_sap == 0xAA) && (llc->control == 0x03) && (snap->protocol == htons (0x2000)) && (snap->oui[0] == 0) && (snap->oui[1] == 0) && (snap->oui[2] == 0x0C))); } static inline void bond_sw_if_idx_rewrite (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_buffer_t * b, u32 bond_sw_if_index, u32 * n_rx_packets, u32 * n_rx_bytes) { u16 *ethertype_p, ethertype; ethernet_vlan_header_t *vlan; ethernet_header_t *eth = (ethernet_header_t *) vlib_buffer_get_current (b); (*n_rx_packets)++; *n_rx_bytes += b->current_length; ethertype = clib_mem_unaligned (ð->type, u16); if (!ethernet_frame_is_tagged (ntohs (ethertype))) { // Let some layer2 packets pass through. if (PREDICT_TRUE ((ethertype != htons (ETHERNET_TYPE_SLOW_PROTOCOLS)) && !packet_is_cdp (eth) && (ethertype != htons (ETHERNET_TYPE_802_1_LLDP)))) { /* Change the physical interface to bond interface */ vnet_buffer (b)->sw_if_index[VLIB_RX] = bond_sw_if_index; return; } } else { vlan = (void *) (eth + 1); ethertype_p = &vlan->type; ethertype = clib_mem_unaligned (ethertype_p, u16); if (ethertype == ntohs (ETHERNET_TYPE_VLAN)) { vlan++; ethertype_p = &vlan->type; } ethertype = clib_mem_unaligned (ethertype_p, u16); if (PREDICT_TRUE ((ethertype != htons (ETHERNET_TYPE_SLOW_PROTOCOLS)) && (ethertype != htons (ETHERNET_TYPE_CDP)) && (ethertype != htons (ETHERNET_TYPE_802_1_LLDP)))) { /* Change the physical interface to bond interface */ vnet_buffer (b)->sw_if_index[VLIB_RX] = bond_sw_if_index; return; } } vlib_error_count (vm, node->node_index, BOND_INPUT_ERROR_PASS_THRU, 1); return; } static inline void bond_update_next (vlib_main_t * vm, vlib_node_runtime_t * node, u32 * last_slave_sw_if_index, u32 slave_sw_if_index, u32 * bond_sw_if_index, vlib_buffer_t * b, u32 * next_index, vlib_error_t * error) { slave_if_t *sif; bond_if_t *bif; *next_index = BOND_INPUT_NEXT_DROP; *error = 0; if (PREDICT_TRUE (*last_slave_sw_if_index == slave_sw_if_index)) goto next; *last_slave_sw_if_index = slave_sw_if_index; sif = bond_get_slave_by_sw_if_index (slave_sw_if_index); ALWAYS_ASSERT (sif); bif = bond_get_master_by_dev_instance (sif->bif_dev_instance); ALWAYS_ASSERT (bif); ASSERT (vec_len (bif->slaves)); if (PREDICT_FALSE (bif->admin_up == 0)) { *bond_sw_if_index = slave_sw_if_index; *error = node->errors[BOND_INPUT_ERROR_IF_DOWN]; } if (PREDICT_FALSE ((bif->mode == BOND_MODE_ACTIVE_BACKUP) && vec_len (bif->active_slaves) && (slave_sw_if_index != bif->active_slaves[0]))) { *bond_sw_if_index = slave_sw_if_index; *error = node->errors[BOND_INPUT_ERROR_PASSIVE_IF]; return; } *bond_sw_if_index = bif->sw_if_index; next: vnet_feature_next (next_index, b); } static_always_inline void bond_update_next_x4 (vlib_buffer_t * b0, vlib_buffer_t * b1, vlib_buffer_t * b2, vlib_buffer_t * b3) { u32 tmp0, tm
/*
 * Copyri