/* * 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 #include #include #include /* * The 'DB' of all mcast adjs. * There is only one mcast per-interface per-protocol, so this is a per-interface * vector */ static adj_index_t *adj_mcasts[FIB_PROTOCOL_MAX]; static u32 adj_get_mcast_node (fib_protocol_t proto) { switch (proto) { case FIB_PROTOCOL_IP4: return (ip4_rewrite_mcast_node.index); case FIB_PROTOCOL_IP6: return (ip6_rewrite_mcast_node.index); case FIB_PROTOCOL_MPLS: break; } ASSERT(0); return (0); } /* * adj_mcast_add_or_lock * * The next_hop address here is used for source address selection in the DP. * The mcast adj is added to an interface's connected prefix, the next-hop * passed here is the local prefix on the same interface. */ adj_index_t adj_mcast_add_or_lock (fib_protocol_t proto, vnet_link_t link_type, u32 sw_if_index) { ip_adjacency_t * adj; vec_validate_init_empty(adj_mcasts[proto], sw_if_index, ADJ_INDEX_INVALID); if (ADJ_INDEX_INVALID == adj_mcasts[proto][sw_if_index]) { vnet_main_t *vnm; vnm = vnet_get_main(); adj = adj_alloc(proto); adj->lookup_next_index = IP_LOOKUP_NEXT_MCAST; adj->ia_nh_proto = proto; adj->ia_link = link_type; adj->ia_node_index = adj_get_mcast_node(proto); adj_mcasts[proto][sw_if_index] = adj_get_index(adj); adj_lock(adj_get_index(adj)); vnet_rewrite_init(vnm, sw_if_index, link_type, adj->ia_node_index, vnet_tx_node_index_for_sw_interface(vnm, sw_if_index), &adj->rewrite_header); /* * we need a rewrite where the destination IP address is converted * to the appropriate link-layer address. This is interface specific. * So ask the interface to do it. */ vnet_update_adjacency_for_sw_interface(vnm, sw_if_index, adj_get_index(adj)); } else { adj = adj_get(adj_mcasts[proto][sw_if_index]); adj_lock(adj_get_index(adj)); } adj_delegate_adj_created(adj); return (adj_get_index(adj)); } /** * adj_mcast_update_rewrite * * Update the adjacency's rewrite string. A NULL string implies the * rewrite is reset (i.e. when ARP/ND entry is gone). * NB: the adj being updated may be handling traffic in the DP. */ void adj_mcast_update_rewrite (adj_index_t adj_index, u8 *rewrite, u8 offset) { ip_adjacency_t *adj; ASSERT(ADJ_INDEX_INVALID != adj_index); adj = adj_get(adj_index); /* * update the adj's rewrite string and build the arc * from the rewrite node to the interface's TX node */ adj_nbr_update_rewrite_internal(adj, IP_LOOKUP_NEXT_MCAST, adj_get_mcast_node(adj->ia_nh_proto), vnet_tx_node_index_for_sw_interface( vnet_get_main(), adj->rewrite_header.sw_if_index), rewrite); /* * set the offset corresponding to the mcast IP address rewrite */ adj->rewrite_header.dst_mcast_offset = offset; } /** * adj_mcast_midchain_update_rewrite * * Update the adjacency's rewrite string. A NULL string implies the * rewrite is reset (i.e. when ARP/ND entry is gone). * NB: the adj being updated may be handling traffic in the DP. */ void adj_mcast_midchain_update_rewrite (adj_index_t adj_index, adj_midchain_fixup_t fixup, const void *fixup_data, adj_flags_t flags, u8 *rewrite, u8 offset, u32 mask) { ip_adjacency_t *adj; ASSERT(ADJ_INDEX_INVALID != adj_index); adj = adj_get(adj_index); /* * one time only update. since we don't support changing the tunnel * src,dst, this is all we need. */ ASSERT(adj->lookup_next_index == IP_LOOKUP_NEXT_MCAST); /* * tunnels can always provide a rewrite. */ ASSERT(NULL != rewrite); adj_midchain_setup(adj_index, fixup, fixup_data, flags); /* * update the adj's rewrite string and build the arc * from the rewrite node to the interface's TX node */ adj_nbr_update_rewrite_internal(adj, IP_LOOKUP_NEXT_MCAST_MIDCHAIN, adj_get_mcast_node(adj->ia_nh_proto), vnet_tx_node_index_for_sw_interface( vnet_get_main(), adj->rewrite_header.sw_if_index), rewrite); adj->rewrite_header.dst_mcast_offset = offset; } void adj_mcast_remove (fib_protocol_t proto, u32 sw_if_index) { ASSERT(sw_if_index < vec_len(adj_mcasts[proto])); adj_mcasts[proto][sw_if_index] = ADJ_INDEX_INVALID; } static clib_error_t * adj_mcast_interface_state_change (vnet_main_t * vnm, u32 sw_if_index, u32 flags) { /* * for each mcast on the interface trigger a walk back to the children */ fib_protocol_t proto; ip_adjacency_t *adj; for (proto = FIB_PROTOCOL_IP4; proto <= FIB_PROTOCOL_IP6; proto++) { if (sw_if_index >= vec_len(adj_mcasts[proto]) || ADJ_INDEX_INVALID == adj_mcasts[proto][sw_if_ind