avf: add avf flow framework
[vpp.git] / src / plugins / avf / flow.c
1 /*
2  *------------------------------------------------------------------
3  * Copyright (c) 2020 Intel and/or its affiliates.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *------------------------------------------------------------------
16  */
17
18 #include <stdbool.h>
19 #include <vlib/vlib.h>
20 #include <vppinfra/ring.h>
21 #include <vlib/unix/unix.h>
22 #include <vlib/pci/pci.h>
23 #include <vnet/ethernet/ethernet.h>
24
25 #include <avf/avf.h>
26 #include <avf/avf_advanced_flow.h>
27
28 int
29 avf_fdir_vc_op_callback (void *vc_hdl, enum virthnl_adv_ops vc_op, void *in,
30                          u32 in_len, void *out, u32 out_len)
31 {
32   u32 dev_instance = *(u32 *) vc_hdl;
33   avf_device_t *ad = avf_get_device (dev_instance);
34   clib_error_t *err = 0;
35   int is_add;
36
37   if (vc_op >= VIRTCHNL_ADV_OP_MAX)
38     {
39       return -1;
40     }
41
42   switch (vc_op)
43     {
44     case VIRTCHNL_ADV_OP_ADD_FDIR_FILTER:
45       is_add = 1;
46       break;
47     case VIRTCHNL_ADV_OP_DEL_FDIR_FILTER:
48       is_add = 0;
49       break;
50     default:
51       avf_log_err (ad, "unsupported avf virtual channel opcode %u\n",
52                    (u32) vc_op);
53       return -1;
54     }
55
56   err = avf_program_flow (dev_instance, is_add, in, in_len, out, out_len);
57   if (err != 0)
58     {
59       avf_log_err (ad, "avf fdir program failed: %U", format_clib_error, err);
60       clib_error_free (err);
61       return -1;
62     }
63
64   avf_log_debug (ad, "avf fdir program success");
65   return 0;
66 }
67
68 static int
69 avf_flow_add (u32 dev_instance, vnet_flow_t *f, avf_flow_entry_t *fe)
70 {
71   avf_device_t *ad = avf_get_device (dev_instance);
72   int rv = 0;
73   int ret = 0;
74   u16 src_port = 0, dst_port = 0;
75   u16 src_port_mask = 0, dst_port_mask = 0;
76   u8 protocol = IP_PROTOCOL_RESERVED;
77   bool fate = false;
78   struct avf_flow_error error;
79
80   int layer = 0;
81   int action_count = 0;
82
83   struct avf_fdir_vc_ctx vc_ctx;
84   struct avf_fdir_conf *filter;
85   struct avf_flow_item avf_items[VIRTCHNL_MAX_NUM_PROTO_HDRS];
86   struct avf_flow_action avf_actions[VIRTCHNL_MAX_NUM_ACTIONS];
87
88   struct avf_ipv4_hdr ip4_spec, ip4_mask;
89   struct avf_tcp_hdr tcp_spec, tcp_mask;
90   struct avf_udp_hdr udp_spec, udp_mask;
91   struct avf_gtp_hdr gtp_spec, gtp_mask;
92
93   struct avf_flow_action_queue act_q;
94   struct avf_flow_action_mark act_msk;
95
96   ret = avf_fdir_rcfg_create (&filter, 0, ad->vsi_id, ad->n_rx_queues);
97   if (ret)
98     {
99       rv = VNET_FLOW_ERROR_INTERNAL;
100       goto done;
101     }
102
103   /* init a virtual channel context */
104   vc_ctx.vc_hdl = &dev_instance;
105   vc_ctx.vc_op = avf_fdir_vc_op_callback;
106
107   clib_memset (avf_items, 0, sizeof (avf_actions));
108   clib_memset (avf_actions, 0, sizeof (avf_actions));
109
110   /* Ethernet Layer */
111   avf_items[layer].type = VIRTCHNL_PROTO_HDR_ETH;
112   avf_items[layer].spec = NULL;
113   layer++;
114
115   /* IPv4 Layer */
116   if ((f->type == VNET_FLOW_TYPE_IP4_N_TUPLE) ||
117       (f->type == VNET_FLOW_TYPE_IP4_GTPU))
118     {
119       vnet_flow_ip4_n_tuple_t *t4 = &f->ip4_n_tuple;
120       memset (&ip4_spec, 0, sizeof (ip4_spec));
121       memset (&ip4_mask, 0, sizeof (ip4_mask));
122
123       /* IPv4 Layer */
124       avf_items[layer].type = VIRTCHNL_PROTO_HDR_IPV4;
125       avf_items[layer].spec = &ip4_spec;
126       avf_items[layer].mask = &ip4_mask;
127       layer++;
128
129       src_port = t4->src_port.port;
130       dst_port = t4->dst_port.port;
131       src_port_mask = t4->src_port.mask;
132       dst_port_mask = t4->dst_port.mask;
133       protocol = t4->protocol.prot;
134
135       if (t4->src_addr.mask.as_u32)
136         {
137           ip4_spec.src_addr = t4->src_addr.addr.as_u32;
138           ip4_mask.src_addr = t4->src_addr.mask.as_u32;
139         }
140       if (t4->dst_addr.mask.as_u32)
141         {
142           ip4_spec.dst_addr = t4->dst_addr.addr.as_u32;
143           ip4_mask.dst_addr = t4->dst_addr.mask.as_u32;
144         }
145     }
146
147   if (protocol == IP_PROTOCOL_TCP)
148     {
149       memset (&tcp_spec, 0, sizeof (tcp_spec));
150       memset (&tcp_mask, 0, sizeof (tcp_mask));
151
152       avf_items[layer].type = VIRTCHNL_PROTO_HDR_TCP;
153       avf_items[layer].spec = &tcp_spec;
154       avf_items[layer].mask = &tcp_mask;
155       layer++;
156
157       if (src_port_mask)
158         {
159           tcp_spec.src_port = clib_host_to_net_u16 (src_port);
160           tcp_mask.src_port = clib_host_to_net_u16 (src_port_mask);
161         }
162       if (dst_port_mask)
163         {
164           tcp_spec.dst_port = clib_host_to_net_u16 (dst_port);
165           tcp_mask.dst_port = clib_host_to_net_u16 (dst_port_mask);
166         }
167     }
168   else if (protocol == IP_PROTOCOL_UDP)
169     {
170       memset (&udp_spec, 0, sizeof (udp_spec));
171       memset (&udp_mask, 0, sizeof (udp_mask));
172
173       avf_items[layer].type = VIRTCHNL_PROTO_HDR_UDP;
174       avf_items[layer].spec = &udp_spec;
175       avf_items[layer].mask = &udp_mask;
176       layer++;
177
178       if (src_port_mask)
179         {
180           udp_spec.src_port = clib_host_to_net_u16 (src_port);
181           udp_mask.src_port = clib_host_to_net_u16 (src_port_mask);
182         }
183       if (dst_port_mask)
184         {
185           udp_spec.dst_port = clib_host_to_net_u16 (dst_port);
186           udp_mask.dst_port = clib_host_to_net_u16 (dst_port_mask);
187         }
188     }
189   else
190     {
191       rv = VNET_FLOW_ERROR_NOT_SUPPORTED;
192       goto done;
193     }
194
195   if (f->type == VNET_FLOW_TYPE_IP4_GTPU)
196     {
197
198       memset (&gtp_spec, 0, sizeof (gtp_spec));
199       memset (&gtp_mask, 0, sizeof (gtp_mask));
200
201       vnet_flow_ip4_gtpu_t *gu = &f->ip4_gtpu;
202       gtp_spec.teid = clib_host_to_net_u32 (gu->teid);
203       gtp_mask.teid = ~0;
204
205       avf_items[layer].type = VIRTCHNL_PROTO_HDR_GTPU_IP;
206       avf_items[layer].spec = &gtp_spec;
207       avf_items[layer].mask = &gtp_mask;
208       layer++;
209     }
210
211   /* pattern end flag  */
212   avf_items[layer].type = VIRTCHNL_PROTO_HDR_NONE;
213   ret = avf_fdir_parse_pattern (filter, avf_items, &error);
214   if (ret)
215     {
216       avf_log_err (ad, "avf fdir parse pattern failed: %s", error.message);
217       rv = VNET_FLOW_ERROR_NOT_SUPPORTED;
218       goto done;
219     }
220
221   /* Action */
222   /* Only one 'fate' can be assigned */
223   if (f->actions & VNET_FLOW_ACTION_REDIRECT_TO_QUEUE)
224     {
225       avf_actions[action_count].type = VIRTCHNL_ACTION_QUEUE;
226       avf_actions[action_count].conf = &act_q;
227
228       act_q.index = f->redirect_queue;
229       fate = true;
230       action_count++;
231     }
232
233   if (f->actions & VNET_FLOW_ACTION_DROP)
234     {
235       avf_actions[action_count].type = VIRTCHNL_ACTION_DROP;
236       avf_actions[action_count].conf = NULL;
237
238       if (fate == true)
239         {
240           rv = VNET_FLOW_ERROR_INTERNAL;
241           goto done;
242         }
243       else
244         fate = true;
245
246       action_count++;
247     }
248
249   if (fate == false)
250     {
251       avf_actions[action_count].type = VIRTCHNL_ACTION_PASSTHRU;
252       avf_actions[action_count].conf = NULL;
253
254       fate = true;
255       action_count++;
256     }
257
258   if (f->actions & VNET_FLOW_ACTION_MARK)
259     {
260       avf_actions[action_count].type = VIRTCHNL_ACTION_MARK;
261       avf_actions[action_count].conf = &act_msk;
262       action_count++;
263
264       act_msk.id = fe->mark;
265     }
266
267   /* action end flag */
268   avf_actions[action_count].type = VIRTCHNL_ACTION_NONE;
269
270   /* parse action */
271   ret = avf_fdir_parse_action (avf_actions, filter, &error);
272   if (ret)
273     {
274       avf_log_err (ad, "avf fdir parse action failed: %s", error.message);
275       rv = VNET_FLOW_ERROR_NOT_SUPPORTED;
276       goto done;
277     }
278
279   /* create flow rule, save rule */
280   ret = avf_fdir_rule_create (&vc_ctx, filter);
281
282   if (ret)
283     {
284       avf_log_err (ad, "avf fdir rule create failed: %s",
285                    avf_fdir_prgm_error_decode (ret));
286       rv = VNET_FLOW_ERROR_INTERNAL;
287       goto done;
288     }
289   else
290     {
291       fe->rcfg = filter;
292     }
293 done:
294
295   return rv;
296 }
297
298 int
299 avf_flow_ops_fn (vnet_main_t *vm, vnet_flow_dev_op_t op, u32 dev_instance,
300                  u32 flow_index, uword *private_data)
301 {
302   vnet_flow_t *flow = vnet_get_flow (flow_index);
303   avf_device_t *ad = avf_get_device (dev_instance);
304   avf_flow_entry_t *fe = NULL;
305   avf_flow_lookup_entry_t *fle = NULL;
306   int rv = 0;
307
308   if ((ad->feature_bitmap & VIRTCHNL_VF_OFFLOAD_FDIR_PF) == 0)
309     {
310       rv = VNET_FLOW_ERROR_NOT_SUPPORTED;
311       goto done;
312     }
313
314   if (op == VNET_FLOW_DEV_OP_ADD_FLOW)
315     {
316       pool_get (ad->flow_entries, fe);
317       fe->flow_index = flow->index;
318
319       /* if we need to mark packets, assign one mark */
320       if (flow->actions &
321           (VNET_FLOW_ACTION_MARK | VNET_FLOW_ACTION_REDIRECT_TO_NODE |
322            VNET_FLOW_ACTION_BUFFER_ADVANCE))
323         {
324           /* reserve slot 0 */
325           if (ad->flow_lookup_entries == 0)
326             pool_get_aligned (ad->flow_lookup_entries, fle,
327                               CLIB_CACHE_LINE_BYTES);
328           pool_get_aligned (ad->flow_lookup_entries, fle,
329                             CLIB_CACHE_LINE_BYTES);
330           fe->mark = fle - ad->flow_lookup_entries;
331
332           /* install entry in the lookup table */
333           clib_memset (fle, -1, sizeof (*fle));
334           if (flow->actions & VNET_FLOW_ACTION_MARK)
335             fle->flow_id = flow->mark_flow_id;
336           if (flow->actions & VNET_FLOW_ACTION_REDIRECT_TO_NODE)
337             fle->next_index = flow->redirect_device_input_next_index;
338           if (flow->actions & VNET_FLOW_ACTION_BUFFER_ADVANCE)
339             fle->buffer_advance = flow->buffer_advance;
340
341           if ((ad->flags & AVF_DEVICE_F_RX_FLOW_OFFLOAD) == 0)
342             {
343               ad->flags |= AVF_DEVICE_F_RX_FLOW_OFFLOAD;
344             }
345         }
346       else
347         fe->mark = 0;
348
349       switch (flow->type)
350         {
351         case VNET_FLOW_TYPE_IP4_N_TUPLE:
352         case VNET_FLOW_TYPE_IP4_GTPU:
353           if ((rv = avf_flow_add (dev_instance, flow, fe)))
354             goto done;
355           break;
356         default:
357           rv = VNET_FLOW_ERROR_NOT_SUPPORTED;
358           goto done;
359         }
360
361       *private_data = fe - ad->flow_entries;
362     }
363   else if (op == VNET_FLOW_DEV_OP_DEL_FLOW)
364     {
365       fe = vec_elt_at_index (ad->flow_entries, *private_data);
366
367       struct avf_fdir_vc_ctx ctx;
368       ctx.vc_hdl = &dev_instance;
369       ctx.vc_op = avf_fdir_vc_op_callback;
370
371       rv = avf_fdir_rule_destroy (&ctx, fe->rcfg);
372       if (rv)
373         return VNET_FLOW_ERROR_INTERNAL;
374
375       if (fe->mark)
376         {
377           fle = pool_elt_at_index (ad->flow_lookup_entries, fe->mark);
378           clib_memset (fle, -1, sizeof (*fle));
379           pool_put_index (ad->flow_lookup_entries, fe->mark);
380         }
381
382       (void) avf_fdir_rcfg_destroy (fe->rcfg);
383       clib_memset (fe, 0, sizeof (*fe));
384       pool_put (ad->flow_entries, fe);
385       goto disable_rx_offload;
386     }
387   else
388     return VNET_FLOW_ERROR_NOT_SUPPORTED;
389
390 done:
391   if (rv)
392     {
393       if (fe)
394         {
395           clib_memset (fe, 0, sizeof (*fe));
396           pool_put (ad->flow_entries, fe);
397         }
398
399       if (fle)
400         {
401           clib_memset (fle, -1, sizeof (*fle));
402           pool_put (ad->flow_lookup_entries, fle);
403         }
404     }
405 disable_rx_offload:
406   if ((ad->flags & AVF_DEVICE_F_RX_FLOW_OFFLOAD) != 0 &&
407       pool_elts (ad->flow_entries) == 0)
408     {
409       ad->flags &= ~AVF_DEVICE_F_RX_FLOW_OFFLOAD;
410     }
411
412   return rv;
413 }
414
415 /*
416  * fd.io coding-style-patch-verification: ON
417  *
418  * Local Variables:
419  * eval: (c-set-style "gnu")
420  * End:
421  */