VPP-237 Coding standards cleanup
[vpp.git] / vpp / app / l2t_ip6.c
1 /*
2  * Copyright (c) 2015 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 #include <vnet/vnet.h>
16 #include <vnet/ip/ip.h>
17 #include <vnet/ethernet/ethernet.h>
18
19 #if DPDK == 0
20 #include <vnet/devices/pci/ixgev.h>
21 #include <vnet/devices/pci/ixge.h>
22 #include <vnet/devices/pci/ige.h>
23 #include <vnet/devices/pci/vice.h>
24 #else
25 #include <vnet/devices/dpdk/dpdk.h>
26 #endif
27
28 #include <vppinfra/error.h>
29 #include <vppinfra/hash.h>
30 #include <app/l2t.h>
31
32 l2t_main_t l2t_main;
33
34 /* Statistics (not really errors) */
35 #define foreach_l2t_ip6_error                                   \
36 _(USER_TO_NETWORK, "User (v6) to L2 network pkts")              \
37 _(SESSION_ID_MISMATCH, "l2tpv3 local session id mismatches")    \
38 _(COOKIE_MISMATCH, "l2tpv3 local cookie mismatches")
39
40 static char *l2t_ip6_error_strings[] = {
41 #define _(sym,string) string,
42   foreach_l2t_ip6_error
43 #undef _
44 };
45
46 typedef enum
47 {
48 #define _(sym,str) L2T_IP6_ERROR_##sym,
49   foreach_l2t_ip6_error
50 #undef _
51     L2T_IP6_N_ERROR,
52 } l2t_ip6_error_t;
53
54 /*
55  * Packets go to ip6-input when they don't match a mapping,
56  * example: v6 neighbor discovery. They go to ip4-input
57  * when they do match, and are decapsulated.
58  */
59 typedef enum
60 {
61   L2T_IP6_NEXT_DROP,
62   L2T_IP6_NEXT_IP6_INPUT,
63   L2T_IP6_N_NEXT,
64   /* Pseudo next, fixed in last_stage */
65   L2T_IP6_NEXT_OUTPUT = L2T_IP6_N_NEXT,
66 } l2t_ip6_next_t;
67
68 vlib_node_registration_t l2t_ip6_node;
69
70 #define NSTAGES 3
71
72 static inline void
73 stage0 (vlib_main_t * vm, vlib_node_runtime_t * node, u32 buffer_index)
74 {
75   vlib_buffer_t *b = vlib_get_buffer (vm, buffer_index);
76   vlib_prefetch_buffer_header (b, STORE);
77   /* l2tpv3 header is a long way away, need 2 cache lines */
78   CLIB_PREFETCH (b->data, 2 * CLIB_CACHE_LINE_BYTES, STORE);
79 }
80
81 static inline void
82 stage1 (vlib_main_t * vm, vlib_node_runtime_t * node, u32 bi)
83 {
84   vlib_buffer_t *b = vlib_get_buffer (vm, bi);
85   l2t_main_t *lm = &l2t_main;
86   ip6_header_t *ip6 = vlib_buffer_get_current (b);
87   u32 session_index;
88   uword *p;
89   l2tpv3_header_t *l2t;
90
91   /* Not L2tpv3 (0x73, 0t115)? Use the normal path. */
92   if (PREDICT_FALSE (ip6->protocol != 0x73))
93     {
94       vnet_buffer (b)->l2t.next_index = L2T_IP6_NEXT_IP6_INPUT;
95       return;
96     }
97
98   /* Make up your minds, people... */
99   switch (lm->lookup_type)
100     {
101     case L2T_LOOKUP_SRC_ADDRESS:
102       p = hash_get_mem (lm->session_by_src_address, &ip6->src_address);
103       break;
104     case L2T_LOOKUP_DST_ADDRESS:
105       p = hash_get_mem (lm->session_by_dst_address, &ip6->dst_address);
106       break;
107     case L2T_LOOKUP_SESSION_ID:
108       l2t = (l2tpv3_header_t *) (ip6 + 1);
109       p = hash_get (lm->session_by_session_id, l2t->session_id);
110       break;
111     default:
112       ASSERT (0);
113     }
114
115   if (PREDICT_FALSE (p == 0))
116     {
117       vnet_buffer (b)->l2t.next_index = L2T_IP6_NEXT_IP6_INPUT;
118       return;
119     }
120   else
121     {
122       session_index = p[0];
123     }
124
125   /* Remember mapping index, prefetch the mini counter */
126   vnet_buffer (b)->l2t.next_index = L2T_IP6_NEXT_OUTPUT;
127   vnet_buffer (b)->l2t.session_index = session_index;
128
129   /* Each mapping has 2 x (pkt, byte) counters, hence the shift */
130   CLIB_PREFETCH (lm->counter_main.mini + (p[0] << 1), CLIB_CACHE_LINE_BYTES,
131                  STORE);
132 }
133
134 static inline u32
135 last_stage (vlib_main_t * vm, vlib_node_runtime_t * node, u32 bi)
136 {
137   vlib_buffer_t *b = vlib_get_buffer (vm, bi);
138   l2t_main_t *lm = &l2t_main;
139   ip6_header_t *ip6 = vlib_buffer_get_current (b);
140   vlib_node_t *n = vlib_get_node (vm, l2t_ip6_node.index);
141   u32 node_counter_base_index = n->error_heap_index;
142   vlib_error_main_t *em = &vm->error_main;
143   ethernet_header_t *l2_payload;
144   l2tpv3_header_t *l2t;         /* original l2 header */
145   ethernet_vlan_header_t *vh;   /* synthesized 802.1q vlan header */
146   u32 counter_index;
147   l2t_session_t *session;
148   u16 payload_ethertype;
149   u8 dst_mac_address[6];
150   u8 src_mac_address[6];
151   u8 *vlan_header_pos;
152
153   /* Other-than-output pkt? We're done... */
154   if (vnet_buffer (b)->l2t.next_index != L2T_IP6_NEXT_OUTPUT)
155     return vnet_buffer (b)->l2t.next_index;
156
157   em->counters[node_counter_base_index + L2T_IP6_ERROR_USER_TO_NETWORK] += 1;
158
159   counter_index =
160     session_index_to_counter_index (vnet_buffer (b)->l2t.session_index,
161                                     SESSION_COUNTER_USER_TO_NETWORK);
162
163   /* per-mapping byte stats include the ethernet header */
164   vlib_increment_combined_counter (&lm->counter_main, counter_index,
165                                    1 /* packet_increment */ ,
166                                    vlib_buffer_length_in_chain (vm, b) +
167                                    sizeof (ethernet_header_t));
168
169   session = pool_elt_at_index (lm->sessions,
170                                vnet_buffer (b)->l2t.session_index);
171
172   /* build the 802.1q encaps. Advance past ip6 and l2tpv3 hds */
173   vlib_buffer_advance (b, sizeof (*ip6));
174   l2t = vlib_buffer_get_current (b);
175
176   /* $$$ wonder if we really need these checks... */
177   if (PREDICT_FALSE (l2t->session_id != session->local_session_id))
178     {
179       b->error =
180         lm->ip6_error_node->errors[L2T_IP6_ERROR_SESSION_ID_MISMATCH];
181       return L2T_IP6_NEXT_DROP;
182     }
183
184   if (PREDICT_FALSE (!((l2t->cookie == session->local_cookie) ||
185                        ((session->cookie_flags & L2TP_COOKIE_ROLLOVER_LOCAL)
186                         && (l2t->cookie == session->lcl_ro_cookie)))))
187     {
188       b->error = lm->ip6_error_node->errors[L2T_IP6_ERROR_COOKIE_MISMATCH];
189       return L2T_IP6_NEXT_DROP;
190     }
191
192   vnet_buffer (b)->sw_if_index[VLIB_TX] = session->l2_output_sw_if_index;
193
194   vlib_buffer_advance (b, sizeof (*l2t));
195
196   /* point at currrent l2 hdr */
197   l2_payload = vlib_buffer_get_current (b);
198
199   /* $$$$ rework for speed */
200
201   /* Save type */
202   payload_ethertype = l2_payload->type;
203
204   /* Save src/dst MAC addresses */
205 #define _(i)  dst_mac_address[i] = l2_payload->dst_address[i];
206   _(0) _(1) _(2) _(3) _(4) _(5);
207 #undef _
208 #define _(i)  src_mac_address[i] = l2_payload->src_address[i];
209   _(0) _(1) _(2) _(3) _(4) _(5);
210 #undef _
211
212   /* Punch in space for 802.1q tag */
213   vlib_buffer_advance (b, -4);
214   l2_payload = vlib_buffer_get_current (b);
215
216   /* Restore MAC addresses */
217 #define _(i)  l2_payload->dst_address[i] = dst_mac_address[i];
218   _(0) _(1) _(2) _(3) _(4) _(5);
219 #undef _
220 #define _(i)  l2_payload->src_address[i] = src_mac_address[i];
221   _(0) _(1) _(2) _(3) _(4) _(5);
222 #undef _
223   /* Set (outer) ethertype to 802.1q vlan */
224   l2_payload->type = clib_host_to_net_u16 (0x8100);
225   vlan_header_pos = (u8 *) (l2_payload + 1);
226 #if 0
227   vlan_header_pos = session->l2_sublayer_present ?
228     vlan_header_pos : vlan_header_pos - 4;
229 #endif
230   vh = (ethernet_vlan_header_t *) vlan_header_pos;
231   vh->priority_cfi_and_id = session->vlan_id;
232   vh->type = payload_ethertype;
233
234   if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
235     {
236       l2t_trace_t *t = vlib_add_trace (vm, node, b, sizeof (*t));
237       t->is_user_to_network = 1;
238       t->our_address.as_u64[0] = ip6->dst_address.as_u64[0];
239       t->our_address.as_u64[1] = ip6->dst_address.as_u64[1];
240       t->client_address.as_u64[0] = ip6->src_address.as_u64[0];
241       t->client_address.as_u64[1] = ip6->src_address.as_u64[1];
242       t->session_index = vnet_buffer (b)->l2t.session_index;
243       t->vlan_id_host_byte_order = clib_net_to_host_u16 (session->vlan_id);
244     }
245
246   return session->l2_output_next_index;
247 }
248
249 #include <vnet/pipeline.h>
250
251 static uword
252 ip6_l2t_node_fn (vlib_main_t * vm,
253                  vlib_node_runtime_t * node, vlib_frame_t * frame)
254 {
255   l2t_main_t *lm = &l2t_main;
256   lm->ip6_error_node = vlib_node_get_runtime (vm, l2t_ip6_node.index);
257
258   return dispatch_pipeline (vm, node, frame);
259 }
260
261 /* *INDENT-OFF* */
262 static VLIB_REGISTER_NODE (sw6_ip6_node) = {
263   .function = ip6_l2t_node_fn,
264   .name = "ip6-l2t-input",
265   .vector_size = sizeof (u32),
266   .format_trace = format_l2t_trace,
267   .type = VLIB_NODE_TYPE_INTERNAL,
268
269   .n_errors = ARRAY_LEN(l2t_ip6_error_strings),
270   .error_strings = l2t_ip6_error_strings,
271
272   .n_next_nodes = L2T_IP6_N_NEXT,
273
274   /* edit / add dispositions here */
275   .next_nodes = {
276     [L2T_IP6_NEXT_IP6_INPUT] = "ip6-input",
277     [L2T_IP6_NEXT_DROP] = "error-drop",
278   },
279 };
280 /* *INDENT-ON* */
281
282 VLIB_NODE_FUNCTION_MULTIARCH (sw6_ip6_node, ip6_l2t_node_fn);
283 static clib_error_t *
284 l2tp_config (vlib_main_t * vm, unformat_input_t * input)
285 {
286   l2t_main_t *lm = &l2t_main;
287
288   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
289     {
290       if (unformat (input, "lookup-v6-src"))
291         lm->lookup_type = L2T_LOOKUP_SRC_ADDRESS;
292       else if (unformat (input, "lookup-v6-dst"))
293         lm->lookup_type = L2T_LOOKUP_DST_ADDRESS;
294       else if (unformat (input, "lookup-session-id"))
295         lm->lookup_type = L2T_LOOKUP_SESSION_ID;
296       else
297         return clib_error_return (0, "unknown input `%U'",
298                                   format_unformat_error, input);
299     }
300   return 0;
301 }
302
303 VLIB_CONFIG_FUNCTION (l2tp_config, "l2tp");
304
305 clib_error_t *
306 l2t_ip6_init (vlib_main_t * vm)
307 {
308   l2t_main_t *lm = &l2t_main;
309
310   lm->vnet_main = vnet_get_main ();
311   lm->vlib_main = vm;
312   lm->lookup_type = L2T_LOOKUP_DST_ADDRESS;
313
314   lm->session_by_src_address = hash_create_mem
315     (0, sizeof (ip6_address_t) /* key bytes */ ,
316      sizeof (u32) /* value bytes */ );
317   lm->session_by_dst_address = hash_create_mem
318     (0, sizeof (ip6_address_t) /* key bytes */ ,
319      sizeof (u32) /* value bytes */ );
320   lm->session_by_session_id = hash_create (0, sizeof (uword));
321
322   lm->session_by_vlan_and_rx_sw_if_index = hash_create (0, sizeof (uword));
323
324 #if DPDK == 0
325   vice_set_next_node (VICE_RX_NEXT_IP6_INPUT, "ip6-l2t-input");
326   ixgev_set_next_node (IXGEV_RX_NEXT_IP6_INPUT, "ip6-l2t-input");
327   ixge_set_next_node (IXGE_RX_NEXT_IP6_INPUT, "ip6-l2t-input");
328   ige_set_next_node (IGE_RX_NEXT_IP6_INPUT, "ip6-l2t-input");
329 #else
330   dpdk_set_next_node (DPDK_RX_NEXT_IP6_INPUT, "ip6-l2t-input");
331 #endif
332   return 0;
333 }
334
335 VLIB_INIT_FUNCTION (l2t_ip6_init);
336
337 /*
338  * fd.io coding-style-patch-verification: ON
339  *
340  * Local Variables:
341  * eval: (c-set-style "gnu")
342  * End:
343  */