A Protocol Independent Hierarchical FIB (VPP-352)
[vpp.git] / vnet / vnet / adj / adj_midchain.c
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vnet/adj/adj_nbr.h>
17 #include <vnet/adj/adj_internal.h>
18 #include <vnet/ethernet/arp_packet.h>
19 #include <vnet/dpo/drop_dpo.h>
20 #include <vnet/fib/fib_walk.h>
21
22 static inline u32
23 adj_get_midchain_node (fib_link_t link)
24 {
25     switch (link) {
26     case FIB_LINK_IP4:
27         return (ip4_midchain_node.index);
28     case FIB_LINK_IP6:
29         return (ip6_midchain_node.index);
30     case FIB_LINK_MPLS:
31         return (mpls_midchain_node.index);
32     }
33     ASSERT(0);
34     return (0);
35 }
36
37 /**
38  * adj_nbr_midchain_update_rewrite
39  *
40  * Update the adjacency's rewrite string. A NULL string implies the
41  * rewrite is reset (i.e. when ARP/ND etnry is gone).
42  * NB: the adj being updated may be handling traffic in the DP.
43  */
44 void
45 adj_nbr_midchain_update_rewrite (adj_index_t adj_index,
46                                  u32 post_rewrite_node,
47                                  u8 *rewrite)
48 {
49     ip_adjacency_t *adj;
50
51     ASSERT(ADJ_INDEX_INVALID != adj_index);
52
53     adj = adj_get(adj_index);
54     adj->lookup_next_index = IP_LOOKUP_NEXT_MIDCHAIN;
55     adj->sub_type.midchain.tx_function_node = post_rewrite_node;
56
57     if (NULL != rewrite)
58     {
59         /*
60          * new rewrite provided.
61          * use a dummy rewrite header to get the interface to print into.
62          */
63         ip_adjacency_t dummy;
64         dpo_id_t tmp = DPO_NULL;
65
66         vnet_rewrite_for_tunnel(vnet_get_main(),
67                                 adj->rewrite_header.sw_if_index,
68                                 adj_get_midchain_node(adj->ia_link),
69                                 adj->sub_type.midchain.tx_function_node,
70                                 &dummy.rewrite_header,
71                                 rewrite,
72                                 vec_len(rewrite));
73
74         /*
75          * this is an update of an existing rewrite.
76          * packets are in flight. we'll need to briefly stack on the drop DPO
77          * whilst the rewrite is written, so any packets that see the partial update
78          * are binned.
79          */
80         if (!dpo_id_is_valid(&adj->sub_type.midchain.next_dpo))
81         {
82             /*
83              * not stacked yet. stack on the drop
84              */
85             dpo_stack(DPO_ADJACENCY_MIDCHAIN,
86                       fib_proto_to_dpo(adj->ia_nh_proto),
87                       &adj->sub_type.midchain.next_dpo,
88                       drop_dpo_get(fib_proto_to_dpo(adj->ia_nh_proto)));
89         }
90             
91         dpo_copy(&tmp, &adj->sub_type.midchain.next_dpo);
92         dpo_stack(DPO_ADJACENCY_MIDCHAIN,
93                   fib_proto_to_dpo(adj->ia_nh_proto),
94                   &adj->sub_type.midchain.next_dpo,
95                   drop_dpo_get(fib_proto_to_dpo(adj->ia_nh_proto)));
96
97         CLIB_MEMORY_BARRIER();
98
99         clib_memcpy(&adj->rewrite_header,
100                     &dummy.rewrite_header,
101                     VLIB_BUFFER_PRE_DATA_SIZE);
102
103         CLIB_MEMORY_BARRIER();
104
105         /*
106          * The graph arc used/created here is from the post-rewirte node to the
107          * child's registered node. This is because post adj processing the next
108          * node is the interface's specific node, then the post-write-node (aka
109          * the interface's tx-function) - from there we need to get to the stacked
110          * child's node.
111          */
112         dpo_stack_from_node(adj->sub_type.midchain.tx_function_node,
113                             &adj->sub_type.midchain.next_dpo,
114                             &tmp);
115         dpo_reset(&tmp);
116     }
117     else
118     {
119         ASSERT(0);
120     }
121
122     /*
123      * time for walkies fido.
124      */
125     fib_node_back_walk_ctx_t bw_ctx = {
126         .fnbw_reason = FIB_NODE_BW_REASON_ADJ_UPDATE,
127     };
128
129     fib_walk_sync(FIB_NODE_TYPE_ADJ, adj->heap_handle, &bw_ctx);
130 }
131
132 /**
133  * adj_nbr_midchain_stack
134  */
135 void
136 adj_nbr_midchain_stack (adj_index_t adj_index,
137                         const dpo_id_t *next)
138 {
139     ip_adjacency_t *adj;
140
141     ASSERT(ADJ_INDEX_INVALID != adj_index);
142
143     adj = adj_get(adj_index);
144
145     ASSERT(IP_LOOKUP_NEXT_MIDCHAIN == adj->lookup_next_index);
146
147     dpo_stack_from_node(adj->sub_type.midchain.tx_function_node,
148                         &adj->sub_type.midchain.next_dpo,
149                         next);
150 }
151
152 u8*
153 format_adj_midchain (u8* s, va_list *ap)
154 {
155     index_t index = va_arg(ap, index_t);
156     u32 indent = va_arg(ap, u32);
157     vnet_main_t * vnm = vnet_get_main();
158     ip_adjacency_t * adj = adj_get(index);
159
160     s = format (s, "%U", format_fib_link, adj->ia_link);
161     s = format (s, " via %U ",
162                 format_ip46_address, &adj->sub_type.nbr.next_hop);
163     s = format (s, " %U",
164                 format_vnet_rewrite,
165                 vnm->vlib_main, &adj->rewrite_header,
166                 sizeof (adj->rewrite_data), indent);
167     s = format (s, "\n%Ustacked-on:\n%U%U",
168                 format_white_space, indent,
169                 format_white_space, indent+2,
170                 format_dpo_id, &adj->sub_type.midchain.next_dpo, indent+2);
171
172     return (s);
173 }
174
175 static void
176 adj_dpo_lock (dpo_id_t *dpo)
177 {
178     adj_lock(dpo->dpoi_index);
179 }
180 static void
181 adj_dpo_unlock (dpo_id_t *dpo)
182 {
183     adj_unlock(dpo->dpoi_index);
184 }
185
186 const static dpo_vft_t adj_midchain_dpo_vft = {
187     .dv_lock = adj_dpo_lock,
188     .dv_unlock = adj_dpo_unlock,
189     .dv_format = format_adj_midchain,
190 };
191
192 /**
193  * @brief The per-protocol VLIB graph nodes that are assigned to a midchain
194  *        object.
195  *
196  * this means that these graph nodes are ones from which a midchain is the
197  * parent object in the DPO-graph.
198  */
199 const static char* const midchain_ip4_nodes[] =
200 {
201     "ip4-midchain",
202     NULL,
203 };
204 const static char* const midchain_ip6_nodes[] =
205 {
206     "ip6-midchain",
207     NULL,
208 };
209 const static char* const midchain_mpls_nodes[] =
210 {
211     "mpls-midchain",
212     NULL,
213 };
214
215 const static char* const * const midchain_nodes[DPO_PROTO_NUM] =
216 {
217     [DPO_PROTO_IP4]  = midchain_ip4_nodes,
218     [DPO_PROTO_IP6]  = midchain_ip6_nodes,
219     [DPO_PROTO_MPLS] = midchain_mpls_nodes,
220 };
221
222 void
223 adj_midchain_module_init (void)
224 {
225     dpo_register(DPO_ADJACENCY_MIDCHAIN, &adj_midchain_dpo_vft, midchain_nodes);
226 }