4d074c170fe706819e9d9997b625b298dd10c138
[dpdk.git] / examples / ip_pipeline / pipeline / pipeline_routing.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include <cmdline_parse.h>
35 #include <cmdline_parse_num.h>
36 #include <cmdline_parse_string.h>
37
38 #include "app.h"
39 #include "pipeline_common_fe.h"
40 #include "pipeline_routing.h"
41 #include "parser.h"
42
43 struct app_pipeline_routing_route {
44         struct pipeline_routing_route_key key;
45         struct pipeline_routing_route_data data;
46         void *entry_ptr;
47
48         TAILQ_ENTRY(app_pipeline_routing_route) node;
49 };
50
51 struct app_pipeline_routing_arp_entry {
52         struct pipeline_routing_arp_key key;
53         struct ether_addr macaddr;
54         void *entry_ptr;
55
56         TAILQ_ENTRY(app_pipeline_routing_arp_entry) node;
57 };
58
59 struct pipeline_routing {
60         /* Parameters */
61         uint32_t n_ports_in;
62         uint32_t n_ports_out;
63
64         /* Routes */
65         TAILQ_HEAD(, app_pipeline_routing_route) routes;
66         uint32_t n_routes;
67
68         uint32_t default_route_present;
69         uint32_t default_route_port_id;
70         void *default_route_entry_ptr;
71
72         /* ARP entries */
73         TAILQ_HEAD(, app_pipeline_routing_arp_entry) arp_entries;
74         uint32_t n_arp_entries;
75
76         uint32_t default_arp_entry_present;
77         uint32_t default_arp_entry_port_id;
78         void *default_arp_entry_ptr;
79 };
80
81 static void *
82 pipeline_routing_init(struct pipeline_params *params,
83         __rte_unused void *arg)
84 {
85         struct pipeline_routing *p;
86         uint32_t size;
87
88         /* Check input arguments */
89         if ((params == NULL) ||
90                 (params->n_ports_in == 0) ||
91                 (params->n_ports_out == 0))
92                 return NULL;
93
94         /* Memory allocation */
95         size = RTE_CACHE_LINE_ROUNDUP(sizeof(struct pipeline_routing));
96         p = rte_zmalloc(NULL, size, RTE_CACHE_LINE_SIZE);
97         if (p == NULL)
98                 return NULL;
99
100         /* Initialization */
101         p->n_ports_in = params->n_ports_in;
102         p->n_ports_out = params->n_ports_out;
103
104         TAILQ_INIT(&p->routes);
105         p->n_routes = 0;
106
107         TAILQ_INIT(&p->arp_entries);
108         p->n_arp_entries = 0;
109
110         return p;
111 }
112
113 static int
114 app_pipeline_routing_free(void *pipeline)
115 {
116         struct pipeline_routing *p = pipeline;
117
118         /* Check input arguments */
119         if (p == NULL)
120                 return -1;
121
122         /* Free resources */
123         while (!TAILQ_EMPTY(&p->routes)) {
124                 struct app_pipeline_routing_route *route;
125
126                 route = TAILQ_FIRST(&p->routes);
127                 TAILQ_REMOVE(&p->routes, route, node);
128                 rte_free(route);
129         }
130
131         while (!TAILQ_EMPTY(&p->arp_entries)) {
132                 struct app_pipeline_routing_arp_entry *arp_entry;
133
134                 arp_entry = TAILQ_FIRST(&p->arp_entries);
135                 TAILQ_REMOVE(&p->arp_entries, arp_entry, node);
136                 rte_free(arp_entry);
137         }
138
139         rte_free(p);
140         return 0;
141 }
142
143 static struct app_pipeline_routing_route *
144 app_pipeline_routing_find_route(struct pipeline_routing *p,
145                 const struct pipeline_routing_route_key *key)
146 {
147         struct app_pipeline_routing_route *it, *found;
148
149         found = NULL;
150         TAILQ_FOREACH(it, &p->routes, node) {
151                 if ((key->type == it->key.type) &&
152                         (key->key.ipv4.ip == it->key.key.ipv4.ip) &&
153                         (key->key.ipv4.depth == it->key.key.ipv4.depth)) {
154                         found = it;
155                         break;
156                 }
157         }
158
159         return found;
160 }
161
162 static struct app_pipeline_routing_arp_entry *
163 app_pipeline_routing_find_arp_entry(struct pipeline_routing *p,
164                 const struct pipeline_routing_arp_key *key)
165 {
166         struct app_pipeline_routing_arp_entry *it, *found;
167
168         found = NULL;
169         TAILQ_FOREACH(it, &p->arp_entries, node) {
170                 if ((key->type == it->key.type) &&
171                         (key->key.ipv4.port_id == it->key.key.ipv4.port_id) &&
172                         (key->key.ipv4.ip == it->key.key.ipv4.ip)) {
173                         found = it;
174                         break;
175                 }
176         }
177
178         return found;
179 }
180
181 static void
182 print_route(const struct app_pipeline_routing_route *route)
183 {
184         if (route->key.type == PIPELINE_ROUTING_ROUTE_IPV4) {
185                 const struct pipeline_routing_route_key_ipv4 *key =
186                                 &route->key.key.ipv4;
187
188                 printf("IP Prefix = %" PRIu32 ".%" PRIu32
189                         ".%" PRIu32 ".%" PRIu32 "/%" PRIu32
190                         " => (Port = %" PRIu32,
191
192                         (key->ip >> 24) & 0xFF,
193                         (key->ip >> 16) & 0xFF,
194                         (key->ip >> 8) & 0xFF,
195                         key->ip & 0xFF,
196
197                         key->depth,
198                         route->data.port_id);
199
200                 if (route->data.flags & PIPELINE_ROUTING_ROUTE_ARP)
201                         printf(
202                                 ", Next Hop IP = %" PRIu32 ".%" PRIu32
203                                 ".%" PRIu32 ".%" PRIu32,
204
205                                 (route->data.ethernet.ip >> 24) & 0xFF,
206                                 (route->data.ethernet.ip >> 16) & 0xFF,
207                                 (route->data.ethernet.ip >> 8) & 0xFF,
208                                 route->data.ethernet.ip & 0xFF);
209                 else
210                         printf(
211                                 ", Next Hop HWaddress = %02" PRIx32
212                                 ":%02" PRIx32 ":%02" PRIx32
213                                 ":%02" PRIx32 ":%02" PRIx32
214                                 ":%02" PRIx32,
215
216                                 route->data.ethernet.macaddr.addr_bytes[0],
217                                 route->data.ethernet.macaddr.addr_bytes[1],
218                                 route->data.ethernet.macaddr.addr_bytes[2],
219                                 route->data.ethernet.macaddr.addr_bytes[3],
220                                 route->data.ethernet.macaddr.addr_bytes[4],
221                                 route->data.ethernet.macaddr.addr_bytes[5]);
222
223                 if (route->data.flags & PIPELINE_ROUTING_ROUTE_QINQ)
224                         printf(", QinQ SVLAN = %" PRIu32 " CVLAN = %" PRIu32,
225                                 route->data.l2.qinq.svlan,
226                                 route->data.l2.qinq.cvlan);
227
228                 if (route->data.flags & PIPELINE_ROUTING_ROUTE_MPLS) {
229                         uint32_t i;
230
231                         printf(", MPLS labels");
232                         for (i = 0; i < route->data.l2.mpls.n_labels; i++)
233                                 printf(" %" PRIu32,
234                                         route->data.l2.mpls.labels[i]);
235                 }
236
237                 printf(")\n");
238         }
239 }
240
241 static void
242 print_arp_entry(const struct app_pipeline_routing_arp_entry *entry)
243 {
244         printf("(Port = %" PRIu32 ", IP = %" PRIu32 ".%" PRIu32
245                 ".%" PRIu32 ".%" PRIu32
246                 ") => HWaddress = %02" PRIx32 ":%02" PRIx32 ":%02" PRIx32
247                 ":%02" PRIx32 ":%02" PRIx32 ":%02" PRIx32 "\n",
248
249                 entry->key.key.ipv4.port_id,
250                 (entry->key.key.ipv4.ip >> 24) & 0xFF,
251                 (entry->key.key.ipv4.ip >> 16) & 0xFF,
252                 (entry->key.key.ipv4.ip >> 8) & 0xFF,
253                 entry->key.key.ipv4.ip & 0xFF,
254
255                 entry->macaddr.addr_bytes[0],
256                 entry->macaddr.addr_bytes[1],
257                 entry->macaddr.addr_bytes[2],
258                 entry->macaddr.addr_bytes[3],
259                 entry->macaddr.addr_bytes[4],
260                 entry->macaddr.addr_bytes[5]);
261 }
262
263 static int
264 app_pipeline_routing_route_ls(struct app_params *app, uint32_t pipeline_id)
265 {
266         struct pipeline_routing *p;
267         struct app_pipeline_routing_route *it;
268
269         p = app_pipeline_data_fe(app, pipeline_id, &pipeline_routing);
270         if (p == NULL)
271                 return -EINVAL;
272
273         TAILQ_FOREACH(it, &p->routes, node)
274                 print_route(it);
275
276         if (p->default_route_present)
277                 printf("Default route: port %" PRIu32 " (entry ptr = %p)\n",
278                                 p->default_route_port_id,
279                                 p->default_route_entry_ptr);
280         else
281                 printf("Default: DROP\n");
282
283         return 0;
284 }
285
286 int
287 app_pipeline_routing_add_route(struct app_params *app,
288         uint32_t pipeline_id,
289         struct pipeline_routing_route_key *key,
290         struct pipeline_routing_route_data *data)
291 {
292         struct pipeline_routing *p;
293
294         struct pipeline_routing_route_add_msg_req *req;
295         struct pipeline_routing_route_add_msg_rsp *rsp;
296
297         struct app_pipeline_routing_route *entry;
298
299         int new_entry;
300
301         /* Check input arguments */
302         if ((app == NULL) ||
303                 (key == NULL) ||
304                 (data == NULL))
305                 return -1;
306
307         p = app_pipeline_data_fe(app, pipeline_id, &pipeline_routing);
308         if (p == NULL)
309                 return -1;
310
311         switch (key->type) {
312         case PIPELINE_ROUTING_ROUTE_IPV4:
313         {
314                 uint32_t depth = key->key.ipv4.depth;
315                 uint32_t netmask;
316
317                 /* key */
318                 if ((depth == 0) || (depth > 32))
319                         return -1;
320
321                 netmask = (~0U) << (32 - depth);
322                 key->key.ipv4.ip &= netmask;
323
324                 /* data */
325                 if (data->port_id >= p->n_ports_out)
326                         return -1;
327         }
328         break;
329
330         default:
331                 return -1;
332         }
333
334         /* Find existing rule or allocate new rule */
335         entry = app_pipeline_routing_find_route(p, key);
336         new_entry = (entry == NULL);
337         if (entry == NULL) {
338                 entry = rte_malloc(NULL, sizeof(*entry), RTE_CACHE_LINE_SIZE);
339
340                 if (entry == NULL)
341                         return -1;
342         }
343
344         /* Allocate and write request */
345         req = app_msg_alloc(app);
346         if (req == NULL) {
347                 if (new_entry)
348                         rte_free(entry);
349                 return -1;
350         }
351
352         req->type = PIPELINE_MSG_REQ_CUSTOM;
353         req->subtype = PIPELINE_ROUTING_MSG_REQ_ROUTE_ADD;
354         memcpy(&req->key, key, sizeof(*key));
355         memcpy(&req->data, data, sizeof(*data));
356
357         rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
358         if (rsp == NULL) {
359                 if (new_entry)
360                         rte_free(entry);
361                 return -1;
362         }
363
364         /* Read response and write entry */
365         if (rsp->status ||
366                 (rsp->entry_ptr == NULL) ||
367                 ((new_entry == 0) && (rsp->key_found == 0)) ||
368                 ((new_entry == 1) && (rsp->key_found == 1))) {
369                 app_msg_free(app, rsp);
370                 if (new_entry)
371                         rte_free(entry);
372                 return -1;
373         }
374
375         memcpy(&entry->key, key, sizeof(*key));
376         memcpy(&entry->data, data, sizeof(*data));
377         entry->entry_ptr = rsp->entry_ptr;
378
379         /* Commit entry */
380         if (new_entry) {
381                 TAILQ_INSERT_TAIL(&p->routes, entry, node);
382                 p->n_routes++;
383         }
384
385         /* Message buffer free */
386         app_msg_free(app, rsp);
387         return 0;
388 }
389
390 int
391 app_pipeline_routing_delete_route(struct app_params *app,
392         uint32_t pipeline_id,
393         struct pipeline_routing_route_key *key)
394 {
395         struct pipeline_routing *p;
396
397         struct pipeline_routing_route_delete_msg_req *req;
398         struct pipeline_routing_route_delete_msg_rsp *rsp;
399
400         struct app_pipeline_routing_route *entry;
401
402         /* Check input arguments */
403         if ((app == NULL) ||
404                 (key == NULL))
405                 return -1;
406
407         p = app_pipeline_data_fe(app, pipeline_id, &pipeline_routing);
408         if (p == NULL)
409                 return -1;
410
411         switch (key->type) {
412         case PIPELINE_ROUTING_ROUTE_IPV4:
413         {
414                 uint32_t depth = key->key.ipv4.depth;
415                 uint32_t netmask;
416
417                 /* key */
418                 if ((depth == 0) || (depth > 32))
419                         return -1;
420
421                 netmask = (~0U) << (32 - depth);
422                 key->key.ipv4.ip &= netmask;
423         }
424         break;
425
426         default:
427                 return -1;
428         }
429
430         /* Find rule */
431         entry = app_pipeline_routing_find_route(p, key);
432         if (entry == NULL)
433                 return 0;
434
435         /* Allocate and write request */
436         req = app_msg_alloc(app);
437         if (req == NULL)
438                 return -1;
439
440         req->type = PIPELINE_MSG_REQ_CUSTOM;
441         req->subtype = PIPELINE_ROUTING_MSG_REQ_ROUTE_DEL;
442         memcpy(&req->key, key, sizeof(*key));
443
444         rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
445         if (rsp == NULL)
446                 return -1;
447
448         /* Read response */
449         if (rsp->status || !rsp->key_found) {
450                 app_msg_free(app, rsp);
451                 return -1;
452         }
453
454         /* Remove route */
455         TAILQ_REMOVE(&p->routes, entry, node);
456         p->n_routes--;
457         rte_free(entry);
458
459         /* Free response */
460         app_msg_free(app, rsp);
461
462         return 0;
463 }
464
465 int
466 app_pipeline_routing_add_default_route(struct app_params *app,
467         uint32_t pipeline_id,
468         uint32_t port_id)
469 {
470         struct pipeline_routing *p;
471
472         struct pipeline_routing_route_add_default_msg_req *req;
473         struct pipeline_routing_route_add_default_msg_rsp *rsp;
474
475         /* Check input arguments */
476         if (app == NULL)
477                 return -1;
478
479         p = app_pipeline_data_fe(app, pipeline_id, &pipeline_routing);
480         if (p == NULL)
481                 return -1;
482
483         if (port_id >= p->n_ports_out)
484                 return -1;
485
486         /* Allocate and write request */
487         req = app_msg_alloc(app);
488         if (req == NULL)
489                 return -1;
490
491         req->type = PIPELINE_MSG_REQ_CUSTOM;
492         req->subtype = PIPELINE_ROUTING_MSG_REQ_ROUTE_ADD_DEFAULT;
493         req->port_id = port_id;
494
495         /* Send request and wait for response */
496         rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
497         if (rsp == NULL)
498                 return -1;
499
500         /* Read response and write route */
501         if (rsp->status || (rsp->entry_ptr == NULL)) {
502                 app_msg_free(app, rsp);
503                 return -1;
504         }
505
506         p->default_route_port_id = port_id;
507         p->default_route_entry_ptr = rsp->entry_ptr;
508
509         /* Commit route */
510         p->default_route_present = 1;
511
512         /* Free response */
513         app_msg_free(app, rsp);
514
515         return 0;
516 }
517
518 int
519 app_pipeline_routing_delete_default_route(struct app_params *app,
520         uint32_t pipeline_id)
521 {
522         struct pipeline_routing *p;
523
524         struct pipeline_routing_arp_delete_default_msg_req *req;
525         struct pipeline_routing_arp_delete_default_msg_rsp *rsp;
526
527         /* Check input arguments */
528         if (app == NULL)
529                 return -1;
530
531         p = app_pipeline_data_fe(app, pipeline_id, &pipeline_routing);
532         if (p == NULL)
533                 return -1;
534
535         /* Allocate and write request */
536         req = app_msg_alloc(app);
537         if (req == NULL)
538                 return -1;
539
540         req->type = PIPELINE_MSG_REQ_CUSTOM;
541         req->subtype = PIPELINE_ROUTING_MSG_REQ_ROUTE_DEL_DEFAULT;
542
543         /* Send request and wait for response */
544         rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
545         if (rsp == NULL)
546                 return -1;
547
548         /* Read response and write route */
549         if (rsp->status) {
550                 app_msg_free(app, rsp);
551                 return -1;
552         }
553
554         /* Commit route */
555         p->default_route_present = 0;
556
557         /* Free response */
558         app_msg_free(app, rsp);
559
560         return 0;
561 }
562
563 static int
564 app_pipeline_routing_arp_ls(struct app_params *app, uint32_t pipeline_id)
565 {
566         struct pipeline_routing *p;
567         struct app_pipeline_routing_arp_entry *it;
568
569         p = app_pipeline_data_fe(app, pipeline_id, &pipeline_routing);
570         if (p == NULL)
571                 return -EINVAL;
572
573         TAILQ_FOREACH(it, &p->arp_entries, node)
574                 print_arp_entry(it);
575
576         if (p->default_arp_entry_present)
577                 printf("Default entry: port %" PRIu32 " (entry ptr = %p)\n",
578                                 p->default_arp_entry_port_id,
579                                 p->default_arp_entry_ptr);
580         else
581                 printf("Default: DROP\n");
582
583         return 0;
584 }
585
586 int
587 app_pipeline_routing_add_arp_entry(struct app_params *app, uint32_t pipeline_id,
588                 struct pipeline_routing_arp_key *key,
589                 struct ether_addr *macaddr)
590 {
591         struct pipeline_routing *p;
592
593         struct pipeline_routing_arp_add_msg_req *req;
594         struct pipeline_routing_arp_add_msg_rsp *rsp;
595
596         struct app_pipeline_routing_arp_entry *entry;
597
598         int new_entry;
599
600         /* Check input arguments */
601         if ((app == NULL) ||
602                 (key == NULL) ||
603                 (macaddr == NULL))
604                 return -1;
605
606         p = app_pipeline_data_fe(app, pipeline_id, &pipeline_routing);
607         if (p == NULL)
608                 return -1;
609
610         switch (key->type) {
611         case PIPELINE_ROUTING_ARP_IPV4:
612         {
613                 uint32_t port_id = key->key.ipv4.port_id;
614
615                 /* key */
616                 if (port_id >= p->n_ports_out)
617                         return -1;
618         }
619         break;
620
621         default:
622                 return -1;
623         }
624
625         /* Find existing entry or allocate new */
626         entry = app_pipeline_routing_find_arp_entry(p, key);
627         new_entry = (entry == NULL);
628         if (entry == NULL) {
629                 entry = rte_malloc(NULL, sizeof(*entry), RTE_CACHE_LINE_SIZE);
630
631                 if (entry == NULL)
632                         return -1;
633         }
634
635         /* Message buffer allocation */
636         req = app_msg_alloc(app);
637         if (req == NULL) {
638                 if (new_entry)
639                         rte_free(entry);
640                 return -1;
641         }
642
643         req->type = PIPELINE_MSG_REQ_CUSTOM;
644         req->subtype = PIPELINE_ROUTING_MSG_REQ_ARP_ADD;
645         memcpy(&req->key, key, sizeof(*key));
646         ether_addr_copy(macaddr, &req->macaddr);
647
648         /* Send request and wait for response */
649         rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
650         if (rsp == NULL) {
651                 if (new_entry)
652                         rte_free(entry);
653                 return -1;
654         }
655
656         /* Read response and write entry */
657         if (rsp->status ||
658                 (rsp->entry_ptr == NULL) ||
659                 ((new_entry == 0) && (rsp->key_found == 0)) ||
660                 ((new_entry == 1) && (rsp->key_found == 1))) {
661                 app_msg_free(app, rsp);
662                 if (new_entry)
663                         rte_free(entry);
664                 return -1;
665         }
666
667         memcpy(&entry->key, key, sizeof(*key));
668         ether_addr_copy(macaddr, &entry->macaddr);
669         entry->entry_ptr = rsp->entry_ptr;
670
671         /* Commit entry */
672         if (new_entry) {
673                 TAILQ_INSERT_TAIL(&p->arp_entries, entry, node);
674                 p->n_arp_entries++;
675         }
676
677         /* Message buffer free */
678         app_msg_free(app, rsp);
679         return 0;
680 }
681
682 int
683 app_pipeline_routing_delete_arp_entry(struct app_params *app,
684         uint32_t pipeline_id,
685         struct pipeline_routing_arp_key *key)
686 {
687         struct pipeline_routing *p;
688
689         struct pipeline_routing_arp_delete_msg_req *req;
690         struct pipeline_routing_arp_delete_msg_rsp *rsp;
691
692         struct app_pipeline_routing_arp_entry *entry;
693
694         /* Check input arguments */
695         if ((app == NULL) ||
696                 (key == NULL))
697                 return -1;
698
699         p = app_pipeline_data_fe(app, pipeline_id, &pipeline_routing);
700         if (p == NULL)
701                 return -EINVAL;
702
703         switch (key->type) {
704         case PIPELINE_ROUTING_ARP_IPV4:
705         {
706                 uint32_t port_id = key->key.ipv4.port_id;
707
708                 /* key */
709                 if (port_id >= p->n_ports_out)
710                         return -1;
711         }
712         break;
713
714         default:
715                 return -1;
716         }
717
718         /* Find rule */
719         entry = app_pipeline_routing_find_arp_entry(p, key);
720         if (entry == NULL)
721                 return 0;
722
723         /* Allocate and write request */
724         req = app_msg_alloc(app);
725         if (req == NULL)
726                 return -1;
727
728         req->type = PIPELINE_MSG_REQ_CUSTOM;
729         req->subtype = PIPELINE_ROUTING_MSG_REQ_ARP_DEL;
730         memcpy(&req->key, key, sizeof(*key));
731
732         rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
733         if (rsp == NULL)
734                 return -1;
735
736         /* Read response */
737         if (rsp->status || !rsp->key_found) {
738                 app_msg_free(app, rsp);
739                 return -1;
740         }
741
742         /* Remove entry */
743         TAILQ_REMOVE(&p->arp_entries, entry, node);
744         p->n_arp_entries--;
745         rte_free(entry);
746
747         /* Free response */
748         app_msg_free(app, rsp);
749
750         return 0;
751 }
752
753 int
754 app_pipeline_routing_add_default_arp_entry(struct app_params *app,
755                 uint32_t pipeline_id,
756                 uint32_t port_id)
757 {
758         struct pipeline_routing *p;
759
760         struct pipeline_routing_arp_add_default_msg_req *req;
761         struct pipeline_routing_arp_add_default_msg_rsp *rsp;
762
763         /* Check input arguments */
764         if (app == NULL)
765                 return -1;
766
767         p = app_pipeline_data_fe(app, pipeline_id, &pipeline_routing);
768         if (p == NULL)
769                 return -1;
770
771         if (port_id >= p->n_ports_out)
772                 return -1;
773
774         /* Allocate and write request */
775         req = app_msg_alloc(app);
776         if (req == NULL)
777                 return -1;
778
779         req->type = PIPELINE_MSG_REQ_CUSTOM;
780         req->subtype = PIPELINE_ROUTING_MSG_REQ_ARP_ADD_DEFAULT;
781         req->port_id = port_id;
782
783         /* Send request and wait for response */
784         rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
785         if (rsp == NULL)
786                 return -1;
787
788         /* Read response and write entry */
789         if (rsp->status || rsp->entry_ptr == NULL) {
790                 app_msg_free(app, rsp);
791                 return -1;
792         }
793
794         p->default_arp_entry_port_id = port_id;
795         p->default_arp_entry_ptr = rsp->entry_ptr;
796
797         /* Commit entry */
798         p->default_arp_entry_present = 1;
799
800         /* Free response */
801         app_msg_free(app, rsp);
802
803         return 0;
804 }
805
806 int
807 app_pipeline_routing_delete_default_arp_entry(struct app_params *app,
808         uint32_t pipeline_id)
809 {
810         struct pipeline_routing *p;
811
812         struct pipeline_routing_arp_delete_default_msg_req *req;
813         struct pipeline_routing_arp_delete_default_msg_rsp *rsp;
814
815         /* Check input arguments */
816         if (app == NULL)
817                 return -1;
818
819         p = app_pipeline_data_fe(app, pipeline_id, &pipeline_routing);
820         if (p == NULL)
821                 return -EINVAL;
822
823         /* Allocate and write request */
824         req = app_msg_alloc(app);
825         if (req == NULL)
826                 return -ENOMEM;
827
828         req->type = PIPELINE_MSG_REQ_CUSTOM;
829         req->subtype = PIPELINE_ROUTING_MSG_REQ_ARP_DEL_DEFAULT;
830
831         /* Send request and wait for response */
832         rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
833         if (rsp == NULL)
834                 return -ETIMEDOUT;
835
836         /* Read response and write entry */
837         if (rsp->status) {
838                 app_msg_free(app, rsp);
839                 return rsp->status;
840         }
841
842         /* Commit entry */
843         p->default_arp_entry_present = 0;
844
845         /* Free response */
846         app_msg_free(app, rsp);
847
848         return 0;
849 }
850
851 /*
852  * route
853  *
854  * route add (ARP = ON/OFF, MPLS = ON/OFF, QINQ = ON/OFF):
855  *    p <pipelineid> route add <ipaddr> <depth> port <portid> ether <nhmacaddr>
856  *    p <pipelineid> route add <ipaddr> <depth> port <portid> ether <nhipaddr>
857  *    p <pipelineid> route add <ipaddr> <depth> port <portid> ether <nhmacaddr> qinq <svlan> <cvlan>
858  *    p <pipelineid> route add <ipaddr> <depth> port <portid> ether <nhipaddr> qinq <svlan> <cvlan>
859  *    p <pipelineid> route add <ipaddr> <depth> port <portid> ether <nhmacaddr> mpls <mpls labels>
860  *    p <pipelineid> route add <ipaddr> <depth> port <portid> ether <nhipaddr> mpls <mpls labels>
861  *
862  * route add default:
863  *    p <pipelineid> route add default <portid>
864  *
865  * route del:
866  *    p <pipelineid> route del <ipaddr> <depth>
867  *
868  * route del default:
869  *    p <pipelineid> route del default
870  *
871  * route ls:
872  *    p <pipelineid> route ls
873  */
874
875 struct cmd_route_result {
876         cmdline_fixed_string_t p_string;
877         uint32_t p;
878         cmdline_fixed_string_t route_string;
879         cmdline_multi_string_t multi_string;
880 };
881
882 static void
883 cmd_route_parsed(
884         void *parsed_result,
885         __rte_unused struct cmdline *cl,
886         void *data)
887 {
888         struct cmd_route_result *params = parsed_result;
889         struct app_params *app = data;
890
891         char *tokens[16];
892         uint32_t n_tokens = RTE_DIM(tokens);
893         int status;
894
895         status = parse_tokenize_string(params->multi_string, tokens, &n_tokens);
896         if (status != 0) {
897                 printf(CMD_MSG_TOO_MANY_ARGS, "route");
898                 return;
899         }
900
901         /* route add */
902         if ((n_tokens >= 2) &&
903                 (strcmp(tokens[0], "add") == 0) &&
904                 strcmp(tokens[1], "default")) {
905                 struct pipeline_routing_route_key key;
906                 struct pipeline_routing_route_data route_data;
907                 struct in_addr ipv4, nh_ipv4;
908                 struct ether_addr mac_addr;
909                 uint32_t depth, port_id, svlan, cvlan, i;
910                 uint32_t mpls_labels[PIPELINE_ROUTING_MPLS_LABELS_MAX];
911                 uint32_t n_labels = RTE_DIM(mpls_labels);
912
913                 memset(&key, 0, sizeof(key));
914                 memset(&route_data, 0, sizeof(route_data));
915
916                 if (n_tokens < 7) {
917                         printf(CMD_MSG_NOT_ENOUGH_ARGS, "route add");
918                         return;
919                 }
920
921                 if (parse_ipv4_addr(tokens[1], &ipv4)) {
922                         printf(CMD_MSG_INVALID_ARG, "ipaddr");
923                         return;
924                 }
925
926                 if (parser_read_uint32(&depth, tokens[2])) {
927                         printf(CMD_MSG_INVALID_ARG, "depth");
928                         return;
929                 }
930
931                 if (strcmp(tokens[3], "port")) {
932                         printf(CMD_MSG_ARG_NOT_FOUND, "port");
933                         return;
934                 }
935
936                 if (parser_read_uint32(&port_id, tokens[4])) {
937                         printf(CMD_MSG_INVALID_ARG, "portid");
938                         return;
939                 }
940
941                 if (strcmp(tokens[5], "ether")) {
942                         printf(CMD_MSG_ARG_NOT_FOUND, "ether");
943                         return;
944                 }
945
946                 if (parse_mac_addr(tokens[6], &mac_addr)) {
947                         if (parse_ipv4_addr(tokens[6], &nh_ipv4)) {
948                                 printf(CMD_MSG_INVALID_ARG, "nhmacaddr or nhipaddr");
949                                 return;
950                         }
951
952                         route_data.flags |= PIPELINE_ROUTING_ROUTE_ARP;
953                 }
954
955                 if (n_tokens > 7) {
956                         if (strcmp(tokens[7], "mpls") == 0) {
957                                 if (n_tokens != 9) {
958                                         printf(CMD_MSG_MISMATCH_ARGS, "route add mpls");
959                                         return;
960                                 }
961
962                                 if (parse_mpls_labels(tokens[8], mpls_labels, &n_labels)) {
963                                         printf(CMD_MSG_INVALID_ARG, "mpls labels");
964                                         return;
965                                 }
966
967                                 route_data.flags |= PIPELINE_ROUTING_ROUTE_MPLS;
968                         } else if (strcmp(tokens[7], "qinq") == 0) {
969                                 if (n_tokens != 10) {
970                                         printf(CMD_MSG_MISMATCH_ARGS, "route add qinq");
971                                         return;
972                                 }
973
974                                 if (parser_read_uint32(&svlan, tokens[8])) {
975                                         printf(CMD_MSG_INVALID_ARG, "svlan");
976                                         return;
977                                 }
978                                 if (parser_read_uint32(&cvlan, tokens[9])) {
979                                         printf(CMD_MSG_INVALID_ARG, "cvlan");
980                                         return;
981                                 }
982
983                                 route_data.flags |= PIPELINE_ROUTING_ROUTE_QINQ;
984                         } else {
985                                 printf(CMD_MSG_ARG_NOT_FOUND, "mpls or qinq");
986                                 return;
987                         }
988                 }
989
990                 switch (route_data.flags) {
991                 case 0:
992                         route_data.port_id = port_id;
993                         route_data.ethernet.macaddr = mac_addr;
994                         break;
995
996                 case PIPELINE_ROUTING_ROUTE_ARP:
997                         route_data.port_id = port_id;
998                         route_data.ethernet.ip = rte_be_to_cpu_32(nh_ipv4.s_addr);
999                         break;
1000
1001                 case PIPELINE_ROUTING_ROUTE_MPLS:
1002                         route_data.port_id = port_id;
1003                         route_data.ethernet.macaddr = mac_addr;
1004                         for (i = 0; i < n_labels; i++)
1005                                 route_data.l2.mpls.labels[i] = mpls_labels[i];
1006                         route_data.l2.mpls.n_labels = n_labels;
1007                         break;
1008
1009                 case PIPELINE_ROUTING_ROUTE_MPLS | PIPELINE_ROUTING_ROUTE_ARP:
1010                         route_data.port_id = port_id;
1011                         route_data.ethernet.ip = rte_be_to_cpu_32(nh_ipv4.s_addr);
1012                         for (i = 0; i < n_labels; i++)
1013                                 route_data.l2.mpls.labels[i] = mpls_labels[i];
1014                         route_data.l2.mpls.n_labels = n_labels;
1015                         break;
1016
1017                 case PIPELINE_ROUTING_ROUTE_QINQ:
1018                         route_data.port_id = port_id;
1019                         route_data.ethernet.macaddr = mac_addr;
1020                         route_data.l2.qinq.svlan = svlan;
1021                         route_data.l2.qinq.cvlan = cvlan;
1022                         break;
1023
1024                 case PIPELINE_ROUTING_ROUTE_QINQ | PIPELINE_ROUTING_ROUTE_ARP:
1025                 default:
1026                         route_data.port_id = port_id;
1027                         route_data.ethernet.ip = rte_be_to_cpu_32(nh_ipv4.s_addr);
1028                         route_data.l2.qinq.svlan = svlan;
1029                         route_data.l2.qinq.cvlan = cvlan;
1030                         break;
1031                 }
1032
1033                 key.type = PIPELINE_ROUTING_ROUTE_IPV4;
1034                 key.key.ipv4.ip = rte_be_to_cpu_32(ipv4.s_addr);
1035                 key.key.ipv4.depth = depth;
1036
1037                 status = app_pipeline_routing_add_route(app,
1038                         params->p,
1039                         &key,
1040                         &route_data);
1041                 if (status != 0)
1042                         printf(CMD_MSG_FAIL, "route add");
1043
1044                 return;
1045         } /* route add */
1046
1047         /* route add default */
1048         if ((n_tokens >= 2) &&
1049                 (strcmp(tokens[0], "add") == 0) &&
1050                 (strcmp(tokens[1], "default") == 0)) {
1051                 uint32_t port_id;
1052
1053                 if (n_tokens != 3) {
1054                         printf(CMD_MSG_MISMATCH_ARGS, "route add default");
1055                         return;
1056                 }
1057
1058                 if (parser_read_uint32(&port_id, tokens[2])) {
1059                         printf(CMD_MSG_INVALID_ARG, "portid");
1060                         return;
1061                 }
1062
1063                 status = app_pipeline_routing_add_default_route(app,
1064                         params->p,
1065                         port_id);
1066                 if (status != 0)
1067                         printf(CMD_MSG_FAIL, "route add default");
1068
1069                 return;
1070         } /* route add default */
1071
1072         /* route del*/
1073         if ((n_tokens >= 2) &&
1074                 (strcmp(tokens[0], "del") == 0) &&
1075                 strcmp(tokens[1], "default")) {
1076                 struct pipeline_routing_route_key key;
1077                 struct in_addr ipv4;
1078                 uint32_t depth;
1079
1080                 memset(&key, 0, sizeof(key));
1081
1082                 if (n_tokens != 3) {
1083                         printf(CMD_MSG_MISMATCH_ARGS, "route del");
1084                         return;
1085                 }
1086
1087                 if (parse_ipv4_addr(tokens[1], &ipv4)) {
1088                         printf(CMD_MSG_INVALID_ARG, "ipaddr");
1089                         return;
1090                 }
1091
1092                 if (parser_read_uint32(&depth, tokens[2])) {
1093                         printf(CMD_MSG_INVALID_ARG, "depth");
1094                         return;
1095                 }
1096
1097                 key.type = PIPELINE_ROUTING_ROUTE_IPV4;
1098                 key.key.ipv4.ip = rte_be_to_cpu_32(ipv4.s_addr);
1099                 key.key.ipv4.depth = depth;
1100
1101                 status = app_pipeline_routing_delete_route(app, params->p, &key);
1102                 if (status != 0)
1103                         printf(CMD_MSG_FAIL, "route del");
1104
1105                 return;
1106         } /* route del */
1107
1108         /* route del default */
1109         if ((n_tokens >= 2) &&
1110                 (strcmp(tokens[0], "del") == 0) &&
1111                 (strcmp(tokens[1], "default") == 0)) {
1112                 if (n_tokens != 2) {
1113                         printf(CMD_MSG_MISMATCH_ARGS, "route del default");
1114                         return;
1115                 }
1116
1117                 status = app_pipeline_routing_delete_default_route(app,
1118                         params->p);
1119                 if (status != 0)
1120                         printf(CMD_MSG_FAIL, "route del default");
1121
1122                 return;
1123         } /* route del default */
1124
1125         /* route ls */
1126         if ((n_tokens >= 1) && (strcmp(tokens[0], "ls") == 0)) {
1127                 if (n_tokens != 1) {
1128                         printf(CMD_MSG_MISMATCH_ARGS, "route ls");
1129                         return;
1130                 }
1131
1132                 status = app_pipeline_routing_route_ls(app, params->p);
1133                 if (status != 0)
1134                         printf(CMD_MSG_FAIL, "route ls");
1135
1136                 return;
1137         } /* route ls */
1138
1139         printf(CMD_MSG_MISMATCH_ARGS, "route");
1140 }
1141
1142 static cmdline_parse_token_string_t cmd_route_p_string =
1143         TOKEN_STRING_INITIALIZER(struct cmd_route_result, p_string, "p");
1144
1145 static cmdline_parse_token_num_t cmd_route_p =
1146         TOKEN_NUM_INITIALIZER(struct cmd_route_result, p, UINT32);
1147
1148 static cmdline_parse_token_string_t cmd_route_route_string =
1149         TOKEN_STRING_INITIALIZER(struct cmd_route_result, route_string, "route");
1150
1151 static cmdline_parse_token_string_t cmd_route_multi_string =
1152         TOKEN_STRING_INITIALIZER(struct cmd_route_result, multi_string,
1153         TOKEN_STRING_MULTI);
1154
1155 static cmdline_parse_inst_t cmd_route = {
1156         .f = cmd_route_parsed,
1157         .data = NULL,
1158         .help_str = "route add / add default / del / del default / ls",
1159         .tokens = {
1160                 (void *)&cmd_route_p_string,
1161                 (void *)&cmd_route_p,
1162                 (void *)&cmd_route_route_string,
1163                 (void *)&cmd_route_multi_string,
1164                 NULL,
1165         },
1166 };
1167
1168 /*
1169  * arp
1170  *
1171  * arp add:
1172  *    p <pipelineid> arp add <portid> <ipaddr> <macaddr>
1173  *
1174  * arp add default:
1175  *    p <pipelineid> arp add default <portid>
1176  *
1177  * arp del:
1178  *    p <pipelineid> arp del <portid> <ipaddr>
1179  *
1180  * arp del default:
1181  *    p <pipelineid> arp del default
1182  *
1183  * arp ls:
1184  *    p <pipelineid> arp ls
1185  */
1186
1187 struct cmd_arp_result {
1188         cmdline_fixed_string_t p_string;
1189         uint32_t p;
1190         cmdline_fixed_string_t arp_string;
1191         cmdline_multi_string_t multi_string;
1192 };
1193
1194 static void
1195 cmd_arp_parsed(
1196         void *parsed_result,
1197         __rte_unused struct cmdline *cl,
1198         void *data)
1199 {
1200         struct cmd_arp_result *params = parsed_result;
1201         struct app_params *app = data;
1202
1203         char *tokens[16];
1204         uint32_t n_tokens = RTE_DIM(tokens);
1205         int status;
1206
1207         status = parse_tokenize_string(params->multi_string, tokens, &n_tokens);
1208         if (status != 0) {
1209                 printf(CMD_MSG_TOO_MANY_ARGS, "arp");
1210                 return;
1211         }
1212
1213         /* arp add */
1214         if ((n_tokens >= 2) &&
1215                 (strcmp(tokens[0], "add") == 0) &&
1216                 strcmp(tokens[1], "default")) {
1217                 struct pipeline_routing_arp_key key;
1218                 struct in_addr ipv4;
1219                 struct ether_addr mac_addr;
1220                 uint32_t port_id;
1221
1222                 memset(&key, 0, sizeof(key));
1223
1224                 if (n_tokens != 4) {
1225                         printf(CMD_MSG_MISMATCH_ARGS, "arp add");
1226                         return;
1227                 }
1228
1229                 if (parser_read_uint32(&port_id, tokens[1])) {
1230                         printf(CMD_MSG_INVALID_ARG, "portid");
1231                         return;
1232                 }
1233
1234                 if (parse_ipv4_addr(tokens[2], &ipv4)) {
1235                         printf(CMD_MSG_INVALID_ARG, "ipaddr");
1236                         return;
1237                 }
1238
1239                 if (parse_mac_addr(tokens[3], &mac_addr)) {
1240                         printf(CMD_MSG_INVALID_ARG, "macaddr");
1241                         return;
1242                 }
1243
1244                 key.type = PIPELINE_ROUTING_ARP_IPV4;
1245                 key.key.ipv4.port_id = port_id;
1246                 key.key.ipv4.ip = rte_be_to_cpu_32(ipv4.s_addr);
1247
1248                 status = app_pipeline_routing_add_arp_entry(app,
1249                         params->p,
1250                         &key,
1251                         &mac_addr);
1252                 if (status != 0)
1253                         printf(CMD_MSG_FAIL, "arp add");
1254
1255                 return;
1256         } /* arp add */
1257
1258         /* arp add default */
1259         if ((n_tokens >= 2) &&
1260                 (strcmp(tokens[0], "add") == 0) &&
1261                 (strcmp(tokens[1], "default") == 0)) {
1262                 uint32_t port_id;
1263
1264                 if (n_tokens != 3) {
1265                         printf(CMD_MSG_MISMATCH_ARGS, "arp add default");
1266                         return;
1267                 }
1268
1269                 if (parser_read_uint32(&port_id, tokens[2])) {
1270                         printf(CMD_MSG_INVALID_ARG, "portid");
1271                         return;
1272                 }
1273
1274                 status = app_pipeline_routing_add_default_arp_entry(app,
1275                         params->p,
1276                         port_id);
1277                 if (status != 0)
1278                         printf(CMD_MSG_FAIL, "arp add default");
1279
1280                 return;
1281         } /* arp add default */
1282
1283         /* arp del*/
1284         if ((n_tokens >= 2) &&
1285                 (strcmp(tokens[0], "del") == 0) &&
1286                 strcmp(tokens[1], "default")) {
1287                 struct pipeline_routing_arp_key key;
1288                 struct in_addr ipv4;
1289                 uint32_t port_id;
1290
1291                 memset(&key, 0, sizeof(key));
1292
1293                 if (n_tokens != 3) {
1294                         printf(CMD_MSG_MISMATCH_ARGS, "arp del");
1295                         return;
1296                 }
1297
1298                 if (parser_read_uint32(&port_id, tokens[1])) {
1299                         printf(CMD_MSG_INVALID_ARG, "portid");
1300                         return;
1301                 }
1302
1303                 if (parse_ipv4_addr(tokens[2], &ipv4)) {
1304                         printf(CMD_MSG_INVALID_ARG, "ipaddr");
1305                         return;
1306                 }
1307
1308                 key.type = PIPELINE_ROUTING_ARP_IPV4;
1309                 key.key.ipv4.ip = rte_be_to_cpu_32(ipv4.s_addr);
1310                 key.key.ipv4.port_id = port_id;
1311
1312                 status = app_pipeline_routing_delete_arp_entry(app,
1313                         params->p,
1314                         &key);
1315                 if (status != 0)
1316                         printf(CMD_MSG_FAIL, "arp del");
1317
1318                 return;
1319         } /* arp del */
1320
1321         /* arp del default */
1322         if ((n_tokens >= 2) &&
1323                 (strcmp(tokens[0], "del") == 0) &&
1324                 (strcmp(tokens[1], "default") == 0)) {
1325                         if (n_tokens != 2) {
1326                                 printf(CMD_MSG_MISMATCH_ARGS, "arp del default");
1327                                 return;
1328                         }
1329
1330                         status = app_pipeline_routing_delete_default_arp_entry(app,
1331                                 params->p);
1332                         if (status != 0)
1333                                 printf(CMD_MSG_FAIL, "arp del default");
1334
1335                         return;
1336         } /* arp del default */
1337
1338         /* arp ls */
1339         if ((n_tokens >= 1) && (strcmp(tokens[0], "ls") == 0)) {
1340                 if (n_tokens != 1) {
1341                         printf(CMD_MSG_MISMATCH_ARGS, "arp ls");
1342                         return;
1343                 }
1344
1345                 status = app_pipeline_routing_arp_ls(app, params->p);
1346                 if (status != 0)
1347                         printf(CMD_MSG_FAIL, "arp ls");
1348
1349                 return;
1350         } /* arp ls */
1351
1352         printf(CMD_MSG_FAIL, "arp");
1353 }
1354
1355 static cmdline_parse_token_string_t cmd_arp_p_string =
1356         TOKEN_STRING_INITIALIZER(struct cmd_arp_result, p_string, "p");
1357
1358 static cmdline_parse_token_num_t cmd_arp_p =
1359         TOKEN_NUM_INITIALIZER(struct cmd_arp_result, p, UINT32);
1360
1361 static cmdline_parse_token_string_t cmd_arp_arp_string =
1362         TOKEN_STRING_INITIALIZER(struct cmd_arp_result, arp_string, "arp");
1363
1364 static cmdline_parse_token_string_t cmd_arp_multi_string =
1365         TOKEN_STRING_INITIALIZER(struct cmd_arp_result, multi_string,
1366         TOKEN_STRING_MULTI);
1367
1368 static cmdline_parse_inst_t cmd_arp = {
1369         .f = cmd_arp_parsed,
1370         .data = NULL,
1371         .help_str = "arp add / add default / del / del default / ls",
1372         .tokens = {
1373                 (void *)&cmd_arp_p_string,
1374                 (void *)&cmd_arp_p,
1375                 (void *)&cmd_arp_arp_string,
1376                 (void *)&cmd_arp_multi_string,
1377                 NULL,
1378         },
1379 };
1380
1381 static cmdline_parse_ctx_t pipeline_cmds[] = {
1382         (cmdline_parse_inst_t *)&cmd_route,
1383         (cmdline_parse_inst_t *)&cmd_arp,
1384         NULL,
1385 };
1386
1387 static struct pipeline_fe_ops pipeline_routing_fe_ops = {
1388         .f_init = pipeline_routing_init,
1389         .f_post_init = NULL,
1390         .f_free = app_pipeline_routing_free,
1391         .f_track = app_pipeline_track_default,
1392         .cmds = pipeline_cmds,
1393 };
1394
1395 struct pipeline_type pipeline_routing = {
1396         .name = "ROUTING",
1397         .be_ops = &pipeline_routing_be_ops,
1398         .fe_ops = &pipeline_routing_fe_ops,
1399 };