urpf: Unicast reverse Path Forwarding (plugin)
[vpp.git] / src / plugins / urpf / urpf_dp.h
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 /*
16  * ip/ip4_source_check.c: IP v4 check source address (unicast RPF check)
17  *
18  * Copyright (c) 2008 Eliot Dresselhaus
19  *
20  * Permission is hereby granted, free of charge, to any person obtaining
21  * a copy of this software and associated documentation files (the
22  * "Software"), to deal in the Software without restriction, including
23  * without limitation the rights to use, copy, modify, merge, publish,
24  * distribute, sublicense, and/or sell copies of the Software, and to
25  * permit persons to whom the Software is furnished to do so, subject to
26  * the following conditions:
27  *
28  * The above copyright notice and this permission notice shall be
29  * included in all copies or substantial portions of the Software.
30  *
31  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34  *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35  *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36  *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38  */
39
40 #ifndef __URPF_DP_H__
41 #define __URPF_DP_H__
42
43 #include <vnet/fib/ip4_fib.h>
44 #include <vnet/fib/ip6_fib.h>
45 #include <vnet/fib/fib_urpf_list.h>
46 #include <vnet/dpo/load_balance.h>
47
48 #include <urpf/urpf.h>
49
50 /**
51  * @file
52  * @brief Unicast Reverse Path forwarding.
53  *
54  * This file contains the interface unicast source check.
55  */
56 typedef struct
57 {
58   index_t urpf;
59 } urpf_trace_t;
60
61 static u8 *
62 format_urpf_trace (u8 * s, va_list * va)
63 {
64   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
65   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
66   urpf_trace_t *t = va_arg (*va, urpf_trace_t *);
67
68   s = format (s, "uRPF:%d", t->urpf);
69
70   return s;
71 }
72
73 #define foreach_urpf_error                 \
74   _(DROP, "uRPF Drop")                     \
75
76 typedef enum urpf_error_t_
77 {
78 #define _(a,b) URPF_ERROR_##a,
79   foreach_urpf_error
80 #undef _
81     URPF_N_ERROR,
82 } urpf_error_t;
83
84 typedef enum
85 {
86   URPF_NEXT_DROP,
87   URPF_N_NEXT,
88 } urpf_next_t;
89
90 static_always_inline uword
91 urpf_inline (vlib_main_t * vm,
92              vlib_node_runtime_t * node,
93              vlib_frame_t * frame,
94              ip_address_family_t af, vlib_dir_t dir, urpf_mode_t mode)
95 {
96   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
97   u16 nexts[VLIB_FRAME_SIZE], *next;
98   u32 n_left, *from;
99
100   from = vlib_frame_vector_args (frame);
101   n_left = frame->n_vectors;
102   b = bufs;
103   next = nexts;
104
105   vlib_get_buffers (vm, from, bufs, n_left);
106
107   while (n_left >= 4)
108     {
109       u32 pass0, lb_index0, pass1, lb_index1;
110       const load_balance_t *lb0, *lb1;
111       u32 fib_index0, fib_index1;
112       const u8 *h0, *h1;
113
114       /* Prefetch next iteration. */
115       {
116         vlib_prefetch_buffer_header (b[2], LOAD);
117         vlib_prefetch_buffer_header (b[3], LOAD);
118         vlib_prefetch_buffer_data (b[2], LOAD);
119         vlib_prefetch_buffer_data (b[3], LOAD);
120       }
121
122       h0 = (u8 *) vlib_buffer_get_current (b[0]);
123       h1 = (u8 *) vlib_buffer_get_current (b[1]);
124
125       if (VLIB_TX == dir)
126         {
127           h0 += vnet_buffer (b[0])->ip.save_rewrite_length;
128           h1 += vnet_buffer (b[1])->ip.save_rewrite_length;
129         }
130
131       if (AF_IP4 == af)
132         {
133           const ip4_header_t *ip0, *ip1;
134
135           ip0 = (ip4_header_t *) h0;
136           ip1 = (ip4_header_t *) h1;
137
138           fib_index0 = ip4_main.fib_index_by_sw_if_index
139             [vnet_buffer (b[0])->sw_if_index[dir]];
140           fib_index1 = ip4_main.fib_index_by_sw_if_index
141             [vnet_buffer (b[1])->sw_if_index[dir]];
142
143           ip4_fib_forwarding_lookup_x2 (fib_index0,
144                                         fib_index1,
145                                         &ip0->src_address,
146                                         &ip1->src_address,
147                                         &lb_index0, &lb_index1);
148           /* Pass multicast. */
149           pass0 = (ip4_address_is_multicast (&ip0->src_address) ||
150                    ip4_address_is_global_broadcast (&ip0->src_address));
151           pass1 = (ip4_address_is_multicast (&ip1->src_address) ||
152                    ip4_address_is_global_broadcast (&ip1->src_address));
153         }
154       else
155         {
156           const ip6_header_t *ip0, *ip1;
157
158           fib_index0 = ip6_main.fib_index_by_sw_if_index
159             [vnet_buffer (b[0])->sw_if_index[dir]];
160           fib_index1 = ip6_main.fib_index_by_sw_if_index
161             [vnet_buffer (b[1])->sw_if_index[dir]];
162
163           ip0 = (ip6_header_t *) h0;
164           ip1 = (ip6_header_t *) h1;
165
166           lb_index0 = ip6_fib_table_fwding_lookup (fib_index0,
167                                                    &ip0->src_address);
168           lb_index1 = ip6_fib_table_fwding_lookup (fib_index1,
169                                                    &ip1->src_address);
170           pass0 = ip6_address_is_multicast (&ip0->src_address);
171           pass1 = ip6_address_is_multicast (&ip1->src_address);
172         }
173
174       lb0 = load_balance_get (lb_index0);
175       lb1 = load_balance_get (lb_index1);
176
177       if (URPF_MODE_STRICT == mode)
178         {
179           /* for RX the check is: would this source adddress be forwarded
180            * out of the interface on which it was recieved, if yes allow.
181            * For TX it's; would this source addres be forwarded out of the
182            * interface through which it is being sent, if yes drop.
183            */
184           int res0, res1;
185
186           res0 = fib_urpf_check (lb0->lb_urpf,
187                                  vnet_buffer (b[0])->sw_if_index[dir]);
188           res1 = fib_urpf_check (lb1->lb_urpf,
189                                  vnet_buffer (b[1])->sw_if_index[dir]);
190
191           if (VLIB_RX == dir)
192             {
193               pass0 |= res0;
194               pass1 |= res1;
195             }
196           else
197             {
198               pass0 |= !res0 && fib_urpf_check_size (lb0->lb_urpf);
199               pass1 |= !res1 && fib_urpf_check_size (lb1->lb_urpf);
200             }
201         }
202       else
203         {
204           pass0 |= fib_urpf_check_size (lb0->lb_urpf);
205           pass1 |= fib_urpf_check_size (lb1->lb_urpf);
206         }
207
208       if (PREDICT_TRUE (pass0))
209         vnet_feature_next_u16 (&next[0], b[0]);
210       else
211         {
212           next[0] = URPF_NEXT_DROP;
213           b[0]->error = node->errors[URPF_ERROR_DROP];
214         }
215       if (PREDICT_TRUE (pass1))
216         vnet_feature_next_u16 (&next[1], b[1]);
217       else
218         {
219           next[1] = URPF_NEXT_DROP;
220           b[1]->error = node->errors[URPF_ERROR_DROP];
221         }
222
223       if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
224         {
225           urpf_trace_t *t;
226
227           t = vlib_add_trace (vm, node, b[0], sizeof (*t));
228           t->urpf = lb0->lb_urpf;
229         }
230       if (b[1]->flags & VLIB_BUFFER_IS_TRACED)
231         {
232           urpf_trace_t *t;
233
234           t = vlib_add_trace (vm, node, b[1], sizeof (*t));
235           t->urpf = lb1->lb_urpf;
236         }
237
238       b += 2;
239       next += 2;
240       n_left -= 2;
241     }
242
243   while (n_left)
244     {
245       u32 pass0, lb_index0, fib_index0;
246       const load_balance_t *lb0;
247       const u8 *h0;
248
249       h0 = (u8 *) vlib_buffer_get_current (b[0]);
250
251       if (VLIB_TX == dir)
252         h0 += vnet_buffer (b[0])->ip.save_rewrite_length;
253
254       if (AF_IP4 == af)
255         {
256           const ip4_header_t *ip0;
257
258           fib_index0 = ip4_main.fib_index_by_sw_if_index
259             [vnet_buffer (b[0])->sw_if_index[dir]];
260           ip0 = (ip4_header_t *) h0;
261
262           lb_index0 = ip4_fib_forwarding_lookup (fib_index0,
263                                                  &ip0->src_address);
264
265           /* Pass multicast. */
266           pass0 = (ip4_address_is_multicast (&ip0->src_address) ||
267                    ip4_address_is_global_broadcast (&ip0->src_address));
268         }
269       else
270         {
271           const ip6_header_t *ip0;
272
273           ip0 = (ip6_header_t *) h0;
274           fib_index0 = ip6_main.fib_index_by_sw_if_index
275             [vnet_buffer (b[0])->sw_if_index[dir]];
276
277           lb_index0 = ip6_fib_table_fwding_lookup (fib_index0,
278                                                    &ip0->src_address);
279           pass0 = ip6_address_is_multicast (&ip0->src_address);
280         }
281
282       lb0 = load_balance_get (lb_index0);
283
284       if (URPF_MODE_STRICT == mode)
285         {
286           int res0;
287
288           res0 = fib_urpf_check (lb0->lb_urpf,
289                                  vnet_buffer (b[0])->sw_if_index[dir]);
290           if (VLIB_RX == dir)
291             pass0 |= res0;
292           else
293             pass0 |= !res0 && fib_urpf_check_size (lb0->lb_urpf);
294         }
295       else
296         pass0 |= fib_urpf_check_size (lb0->lb_urpf);
297
298       if (PREDICT_TRUE (pass0))
299         vnet_feature_next_u16 (&next[0], b[0]);
300       else
301         {
302           next[0] = URPF_NEXT_DROP;
303           b[0]->error = node->errors[URPF_ERROR_DROP];
304         }
305
306       if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
307         {
308           urpf_trace_t *t;
309
310           t = vlib_add_trace (vm, node, b[0], sizeof (*t));
311           t->urpf = lb0->lb_urpf;
312         }
313       b++;
314       next++;
315       n_left--;
316     }
317
318   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
319
320   return frame->n_vectors;
321 }
322
323 #endif
324
325 /*
326  * fd.io coding-style-patch-verification: ON
327  *
328  * Local Variables:
329  * eval: (c-set-style "gnu")
330  * End:
331  */