07d30d9a2f2c027284a89ebf1c191f79ea3b0e48
[vpp.git] / vpp / app / l2t_l2.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 #else
24 #include <vnet/devices/dpdk/dpdk.h>
25 #endif
26
27 #include <vppinfra/error.h>
28 #include <vppinfra/hash.h>
29 #include <app/l2t.h>
30
31 l2t_main_t l2t_main;
32
33 /* Statistics (not really errors) */
34 #define foreach_l2t_l2_error                       \
35 _(NETWORK_TO_USER, "L2 network to user (ip6) pkts")
36
37 static char *l2t_l2_error_strings[] = {
38 #define _(sym,string) string,
39   foreach_l2t_l2_error
40 #undef _
41 };
42
43 typedef enum
44 {
45 #define _(sym,str) L2T_L2_ERROR_##sym,
46   foreach_l2t_l2_error
47 #undef _
48     L2T_L2_N_ERROR,
49 } l2t_l2_error_t;
50
51 /*
52  * Packets go to ethernet-input when they don't match a mapping
53  */
54 typedef enum
55 {
56   L2T_L2_NEXT_DROP,
57   L2T_L2_NEXT_ETHERNET_INPUT,
58   L2T_L2_NEXT_IP6_LOOKUP,
59   L2T_L2_N_NEXT,
60 } l2t_l2_next_t;
61
62 vlib_node_registration_t l2t_l2_node;
63
64 #define NSTAGES 3
65
66 static inline void
67 stage0 (vlib_main_t * vm, vlib_node_runtime_t * node, u32 buffer_index)
68 {
69   vlib_buffer_t *b = vlib_get_buffer (vm, buffer_index);
70   vlib_prefetch_buffer_header (b, STORE);
71   CLIB_PREFETCH (b->data, 2 * CLIB_CACHE_LINE_BYTES, STORE);
72 }
73
74 static inline void
75 stage1 (vlib_main_t * vm, vlib_node_runtime_t * node, u32 bi)
76 {
77   vlib_buffer_t *b = vlib_get_buffer (vm, bi);
78   l2t_main_t *lm = &l2t_main;
79   ethernet_header_t *eh;
80   ethernet_vlan_header_t *vh;
81   u32 session_index;
82   uword *p;
83   uword vlan_and_sw_if_index_key;
84
85   /* just in case, needed to test with the tun/tap device */
86   vlib_buffer_reset (b);
87
88   eh = vlib_buffer_get_current (b);
89
90   /* Not a VLAN pkt? send to ethernet-input... */
91   if (PREDICT_FALSE (eh->type != clib_host_to_net_u16 (0x8100)))
92     {
93       vnet_buffer (b)->l2t.next_index = L2T_L2_NEXT_ETHERNET_INPUT;
94       return;
95     }
96   vh = (ethernet_vlan_header_t *) (eh + 1);
97
98   /* look up session */
99   vlan_and_sw_if_index_key = ((uword) (vh->priority_cfi_and_id) << 32)
100     | vnet_buffer (b)->sw_if_index[VLIB_RX];
101
102   p = hash_get (lm->session_by_vlan_and_rx_sw_if_index,
103                 vlan_and_sw_if_index_key);
104
105   if (PREDICT_FALSE (p == 0))
106     {
107       /* $$$ drop here if not for our MAC? */
108       vnet_buffer (b)->l2t.next_index = L2T_L2_NEXT_ETHERNET_INPUT;
109       return;
110     }
111   else
112     {
113       session_index = p[0];
114     }
115
116   /* Remember mapping index, prefetch the mini counter */
117   vnet_buffer (b)->l2t.next_index = L2T_L2_NEXT_IP6_LOOKUP;
118   vnet_buffer (b)->l2t.session_index = session_index;
119
120   /* Each mapping has 2 x (pkt, byte) counters, hence the shift */
121   CLIB_PREFETCH (lm->counter_main.mini + (p[0] << 1), CLIB_CACHE_LINE_BYTES,
122                  STORE);
123 }
124
125 static inline u32
126 last_stage (vlib_main_t * vm, vlib_node_runtime_t * node, u32 bi)
127 {
128   vlib_buffer_t *b = vlib_get_buffer (vm, bi);
129   l2t_main_t *lm = &l2t_main;
130   ethernet_header_t *eh = vlib_buffer_get_current (b);
131   vlib_node_t *n = vlib_get_node (vm, l2t_l2_node.index);
132   u32 node_counter_base_index = n->error_heap_index;
133   vlib_error_main_t *em = &vm->error_main;
134   l2tpv3_header_t *l2t;         /* l2 header */
135   ethernet_vlan_header_t *vh;   /* 802.1q vlan header */
136   u32 counter_index;
137   l2t_session_t *s;
138   ip6_header_t *ip6;
139   u16 payload_ethertype;
140   u8 dst_mac_address[6];
141   u8 src_mac_address[6];
142   u16 payload_length;
143   i32 backup;
144
145   /* Other-than-output pkt? We're done... */
146   if (vnet_buffer (b)->l2t.next_index != L2T_L2_NEXT_IP6_LOOKUP)
147     return vnet_buffer (b)->l2t.next_index;
148
149   vh = (ethernet_vlan_header_t *) (eh + 1);
150
151   em->counters[node_counter_base_index + L2T_L2_ERROR_NETWORK_TO_USER] += 1;
152
153   counter_index =
154     session_index_to_counter_index (vnet_buffer (b)->l2t.session_index,
155                                     SESSION_COUNTER_NETWORK_TO_USER);
156
157   /* per-mapping byte stats include the ethernet header */
158   vlib_increment_combined_counter (&lm->counter_main, counter_index,
159                                    1 /* packet_increment */ ,
160                                    vlib_buffer_length_in_chain (vm, b) +
161                                    sizeof (ethernet_header_t));
162
163   s = pool_elt_at_index (lm->sessions, vnet_buffer (b)->l2t.session_index);
164
165   /* Save src/dst MAC addresses */
166 #define _(i)  dst_mac_address[i] = eh->dst_address[i];
167   _(0) _(1) _(2) _(3) _(4) _(5);
168 #undef _
169 #define _(i)  src_mac_address[i] = eh->src_address[i];
170   _(0) _(1) _(2) _(3) _(4) _(5);
171 #undef _
172
173   payload_ethertype = vh->type;
174
175   /* Splice out the 802.1q vlan tag */
176   vlib_buffer_advance (b, 4);
177   eh = vlib_buffer_get_current (b);
178
179   /* restore src/dst MAC addresses */
180 #define _(i)   eh->dst_address[i] = dst_mac_address[i];
181   _(0) _(1) _(2) _(3) _(4) _(5);
182 #undef _
183 #define _(i)  eh->src_address[i] = src_mac_address[i];
184   _(0) _(1) _(2) _(3) _(4) _(5);
185 #undef _
186   eh->type = payload_ethertype;
187
188   /* Paint on an l2tpv3 hdr */
189   backup = sizeof (*l2t);
190 #if 0
191   /* back up 4 bytes less if no l2 sublayer */
192   backup -= s->l2_sublayer_present ? 0 : 4;
193 #endif
194
195   vlib_buffer_advance (b, -backup);
196   l2t = vlib_buffer_get_current (b);
197
198   l2t->session_id = s->remote_session_id;
199   l2t->cookie = s->remote_cookie;
200
201 #if 0
202   if (s->l2_sublayer_present)
203     l2t->l2_specific_sublayer = 0;
204 #endif
205
206   /* Paint on an ip6 header */
207   vlib_buffer_advance (b, -(sizeof (*ip6)));
208   ip6 = vlib_buffer_get_current (b);
209
210   ip6->ip_version_traffic_class_and_flow_label =
211     clib_host_to_net_u32 (0x6 << 28);
212
213   /* calculate ip6 payload length */
214   payload_length = vlib_buffer_length_in_chain (vm, b);
215   payload_length -= sizeof (*ip6);
216
217   ip6->payload_length = clib_host_to_net_u16 (payload_length);
218   ip6->protocol = 0x73;         /* l2tpv3 */
219   ip6->hop_limit = 0xff;
220   ip6->src_address.as_u64[0] = s->our_address.as_u64[0];
221   ip6->src_address.as_u64[1] = s->our_address.as_u64[1];
222   ip6->dst_address.as_u64[0] = s->client_address.as_u64[0];
223   ip6->dst_address.as_u64[1] = s->client_address.as_u64[1];
224
225   return L2T_L2_NEXT_IP6_LOOKUP;
226 }
227
228 #include <vnet/pipeline.h>
229
230 static uword
231 l2t_l2_node_fn (vlib_main_t * vm,
232                 vlib_node_runtime_t * node, vlib_frame_t * frame)
233 {
234   return dispatch_pipeline (vm, node, frame);
235 }
236
237 /* *INDENT-OFF* */
238 VLIB_REGISTER_NODE (l2t_l2_node) = {
239   .function = l2t_l2_node_fn,
240   .name = "l2t-l2-input",
241   .vector_size = sizeof (u32),
242   .format_trace = format_l2t_trace,
243   .type = VLIB_NODE_TYPE_INTERNAL,
244
245   .n_errors = ARRAY_LEN(l2t_l2_error_strings),
246   .error_strings = l2t_l2_error_strings,
247
248   .n_next_nodes = L2T_L2_N_NEXT,
249
250   /* edit / add dispositions here */
251   .next_nodes = {
252     [L2T_L2_NEXT_IP6_LOOKUP] = "ip6-lookup",
253     [L2T_L2_NEXT_ETHERNET_INPUT] = "ethernet-input",
254     [L2T_L2_NEXT_DROP] = "error-drop",
255   },
256 };
257 /* *INDENT-ON* */
258
259 VLIB_NODE_FUNCTION_MULTIARCH (l2t_l2_node, l2t_l2_node_fn);
260
261 /*
262  * fd.io coding-style-patch-verification: ON
263  *
264  * Local Variables:
265  * eval: (c-set-style "gnu")
266  * End:
267  */