urpf: Allow locally generated packets on TX
[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 address 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               /* allow locally generated */
202               pass0 |= b[0]->flags & VNET_BUFFER_F_LOCALLY_ORIGINATED;
203               pass1 |= b[1]->flags & VNET_BUFFER_F_LOCALLY_ORIGINATED;
204             }
205         }
206       else
207         {
208           pass0 |= fib_urpf_check_size (lb0->lb_urpf);
209           pass1 |= fib_urpf_check_size (lb1->lb_urpf);
210         }
211
212       if (PREDICT_TRUE (pass0))
213         vnet_feature_next_u16 (&next[0], b[0]);
214       else
215         {
216           next[0] = URPF_NEXT_DROP;
217           b[0]->error = node->errors[URPF_ERROR_DROP];
218         }
219       if (PREDICT_TRUE (pass1))
220         vnet_feature_next_u16 (&next[1], b[1]);
221       else
222         {
223           next[1] = URPF_NEXT_DROP;
224           b[1]->error = node->errors[URPF_ERROR_DROP];
225         }
226
227       if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
228         {
229           urpf_trace_t *t;
230
231           t = vlib_add_trace (vm, node, b[0], sizeof (*t));
232           t->urpf = lb0->lb_urpf;
233         }
234       if (b[1]->flags & VLIB_BUFFER_IS_TRACED)
235         {
236           urpf_trace_t *t;
237
238           t = vlib_add_trace (vm, node, b[1], sizeof (*t));
239           t->urpf = lb1->lb_urpf;
240         }
241
242       b += 2;
243       next += 2;
244       n_left -= 2;
245     }
246
247   while (n_left)
248     {
249       u32 pass0, lb_index0, fib_index0;
250       const load_balance_t *lb0;
251       const u8 *h0;
252
253       h0 = (u8 *) vlib_buffer_get_current (b[0]);
254
255       if (VLIB_TX == dir)
256         h0 += vnet_buffer (b[0])->ip.save_rewrite_length;
257
258       if (AF_IP4 == af)
259         {
260           const ip4_header_t *ip0;
261
262           fib_index0 = ip4_main.fib_index_by_sw_if_index
263             [vnet_buffer (b[0])->sw_if_index[dir]];
264           ip0 = (ip4_header_t *) h0;
265
266           lb_index0 = ip4_fib_forwarding_lookup (fib_index0,
267                                                  &ip0->src_address);
268
269           /* Pass multicast. */
270           pass0 = (ip4_address_is_multicast (&ip0->src_address) ||
271                    ip4_address_is_global_broadcast (&ip0->src_address));
272         }
273       else
274         {
275           const ip6_header_t *ip0;
276
277           ip0 = (ip6_header_t *) h0;
278           fib_index0 = ip6_main.fib_index_by_sw_if_index
279             [vnet_buffer (b[0])->sw_if_index[dir]];
280
281           lb_index0 = ip6_fib_table_fwding_lookup (fib_index0,
282                                                    &ip0->src_address);
283           pass0 = ip6_address_is_multicast (&ip0->src_address);
284         }
285
286       lb0 = load_balance_get (lb_index0);
287
288       if (URPF_MODE_STRICT == mode)
289         {
290           int res0;
291
292           res0 = fib_urpf_check (lb0->lb_urpf,
293                                  vnet_buffer (b[0])->sw_if_index[dir]);
294           if (VLIB_RX == dir)
295             pass0 |= res0;
296           else
297             {
298               pass0 |= !res0 && fib_urpf_check_size (lb0->lb_urpf);
299               pass0 |= b[0]->flags & VNET_BUFFER_F_LOCALLY_ORIGINATED;
300             }
301         }
302       else
303         pass0 |= fib_urpf_check_size (lb0->lb_urpf);
304
305       if (PREDICT_TRUE (pass0))
306         vnet_feature_next_u16 (&next[0], b[0]);
307       else
308         {
309           next[0] = URPF_NEXT_DROP;
310           b[0]->error = node->errors[URPF_ERROR_DROP];
311         }
312
313       if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
314         {
315           urpf_trace_t *t;
316
317           t = vlib_add_trace (vm, node, b[0], sizeof (*t));
318           t->urpf = lb0->lb_urpf;
319         }
320       b++;
321       next++;
322       n_left--;
323     }
324
325   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
326
327   return frame->n_vectors;
328 }
329
330 #endif
331
332 /*
333  * fd.io coding-style-patch-verification: ON
334  *
335  * Local Variables:
336  * eval: (c-set-style "gnu")
337  * End:
338  */