examples/l3fwd: support config file for EM
[dpdk.git] / examples / l3fwd / em_route_parse.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2022 Intel Corporation
3  */
4
5 #include <stdio.h>
6 #include <stdint.h>
7 #include <errno.h>
8 #include <sys/socket.h>
9
10 #include "l3fwd.h"
11 #include "l3fwd_route.h"
12
13 static struct em_rule *em_route_base_v4;
14 static struct em_rule *em_route_base_v6;
15
16 enum {
17         CB_FLD_DST_ADDR,
18         CB_FLD_SRC_ADDR,
19         CB_FLD_DST_PORT,
20         CB_FLD_SRC_PORT,
21         CB_FLD_PROTO,
22         CB_FLD_IF_OUT,
23         CB_FLD_MAX
24 };
25
26 static int
27 em_parse_v6_net(const char *in, uint8_t *v)
28 {
29         int32_t rc;
30
31         /* get address. */
32         rc = inet_pton(AF_INET6, in, v);
33         if (rc != 1)
34                 return -EINVAL;
35
36         return 0;
37 }
38
39 static int
40 em_parse_v6_rule(char *str, struct em_rule *v)
41 {
42         int i, rc;
43         char *s, *sp, *in[CB_FLD_MAX];
44         static const char *dlm = " \t\n";
45         int dim = CB_FLD_MAX;
46         s = str;
47
48         for (i = 0; i != dim; i++, s = NULL) {
49                 in[i] = strtok_r(s, dlm, &sp);
50                 if (in[i] == NULL)
51                         return -EINVAL;
52         }
53
54         rc = em_parse_v6_net(in[CB_FLD_DST_ADDR], v->v6_key.ip_dst);
55         if (rc != 0)
56                 return rc;
57         rc = em_parse_v6_net(in[CB_FLD_SRC_ADDR], v->v6_key.ip_src);
58         if (rc != 0)
59                 return rc;
60
61         /* source port. */
62         GET_CB_FIELD(in[CB_FLD_SRC_PORT], v->v6_key.port_src, 0, UINT16_MAX, 0);
63         /* destination port. */
64         GET_CB_FIELD(in[CB_FLD_DST_PORT], v->v6_key.port_dst, 0, UINT16_MAX, 0);
65         /* protocol. */
66         GET_CB_FIELD(in[CB_FLD_PROTO], v->v6_key.proto, 0, UINT8_MAX, 0);
67         /* out interface. */
68         GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
69
70         return 0;
71 }
72
73 static int
74 em_parse_v4_rule(char *str, struct em_rule *v)
75 {
76         int i, rc;
77         char *s, *sp, *in[CB_FLD_MAX];
78         static const char *dlm = " \t\n";
79         int dim = CB_FLD_MAX;
80         s = str;
81
82         for (i = 0; i != dim; i++, s = NULL) {
83                 in[i] = strtok_r(s, dlm, &sp);
84                 if (in[i] == NULL)
85                         return -EINVAL;
86         }
87
88         rc = inet_pton(AF_INET, in[CB_FLD_DST_ADDR], &(v->v4_key.ip_dst));
89         v->v4_key.ip_dst = ntohl(v->v4_key.ip_dst);
90         if (rc != 1)
91                 return rc;
92
93         rc = inet_pton(AF_INET, in[CB_FLD_SRC_ADDR], &(v->v4_key.ip_src));
94         v->v4_key.ip_src = ntohl(v->v4_key.ip_src);
95         if (rc != 1)
96                 return rc;
97
98         /* source port. */
99         GET_CB_FIELD(in[CB_FLD_SRC_PORT], v->v4_key.port_src, 0, UINT16_MAX, 0);
100         /* destination port. */
101         GET_CB_FIELD(in[CB_FLD_DST_PORT], v->v4_key.port_dst, 0, UINT16_MAX, 0);
102         /* protocol. */
103         GET_CB_FIELD(in[CB_FLD_PROTO], v->v4_key.proto, 0, UINT8_MAX, 0);
104         /* out interface. */
105         GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
106
107         return 0;
108 }
109
110 static int
111 em_add_rules(const char *rule_path,
112                 struct em_rule **proute_base,
113                 int (*parser)(char *, struct em_rule *))
114 {
115         struct em_rule *route_rules;
116         struct em_rule *next;
117         unsigned int route_num = 0;
118         unsigned int route_cnt = 0;
119         char buff[LINE_MAX];
120         FILE *fh;
121         unsigned int i = 0, rule_size = sizeof(*next);
122         int val;
123
124         *proute_base = NULL;
125         fh = fopen(rule_path, "rb");
126         if (fh == NULL)
127                 return -EINVAL;
128
129         while ((fgets(buff, LINE_MAX, fh) != NULL)) {
130                 if (buff[0] == ROUTE_LEAD_CHAR)
131                         route_num++;
132         }
133
134         if (route_num == 0) {
135                 fclose(fh);
136                 return -EINVAL;
137         }
138
139         val = fseek(fh, 0, SEEK_SET);
140         if (val < 0) {
141                 fclose(fh);
142                 return -EINVAL;
143         }
144
145         route_rules = calloc(route_num, rule_size);
146
147         if (route_rules == NULL) {
148                 fclose(fh);
149                 return -EINVAL;
150         }
151
152         i = 0;
153         while (fgets(buff, LINE_MAX, fh) != NULL) {
154                 i++;
155                 if (is_bypass_line(buff))
156                         continue;
157
158                 char s = buff[0];
159
160                 /* Route entry */
161                 if (s == ROUTE_LEAD_CHAR)
162                         next = &route_rules[route_cnt];
163
164                 /* Illegal line */
165                 else {
166                         RTE_LOG(ERR, L3FWD,
167                                 "%s Line %u: should start with leading "
168                                 "char %c\n",
169                                 rule_path, i, ROUTE_LEAD_CHAR);
170                         fclose(fh);
171                         free(route_rules);
172                         return -EINVAL;
173                 }
174
175                 if (parser(buff + 1, next) != 0) {
176                         RTE_LOG(ERR, L3FWD,
177                                 "%s Line %u: parse rules error\n",
178                                 rule_path, i);
179                         fclose(fh);
180                         free(route_rules);
181                         return -EINVAL;
182                 }
183
184                 route_cnt++;
185         }
186
187         fclose(fh);
188
189         *proute_base = route_rules;
190
191         return route_cnt;
192 }
193
194 static int
195 em_add_default_v4_rules(void)
196 {
197         /* populate the LPM IPv4 table */
198         unsigned int i, rule_size = sizeof(*em_route_base_v4);
199         route_num_v4 = RTE_DIM(ipv4_l3fwd_em_route_array);
200
201         em_route_base_v4 = calloc(route_num_v4, rule_size);
202
203         for (i = 0; i < (unsigned int)route_num_v4; i++) {
204                 em_route_base_v4[i].v4_key.ip_dst = ipv4_l3fwd_em_route_array[i].key.ip_dst;
205                 em_route_base_v4[i].v4_key.ip_src = ipv4_l3fwd_em_route_array[i].key.ip_src;
206                 em_route_base_v4[i].v4_key.port_dst = ipv4_l3fwd_em_route_array[i].key.port_dst;
207                 em_route_base_v4[i].v4_key.port_src = ipv4_l3fwd_em_route_array[i].key.port_src;
208                 em_route_base_v4[i].v4_key.proto = ipv4_l3fwd_em_route_array[i].key.proto;
209                 em_route_base_v4[i].if_out = ipv4_l3fwd_em_route_array[i].if_out;
210         }
211         return 0;
212 }
213
214 static int
215 em_add_default_v6_rules(void)
216 {
217         /* populate the LPM IPv6 table */
218         unsigned int i, rule_size = sizeof(*em_route_base_v6);
219         route_num_v6 = RTE_DIM(ipv6_l3fwd_em_route_array);
220
221         em_route_base_v6 = calloc(route_num_v6, rule_size);
222
223         for (i = 0; i < (unsigned int)route_num_v6; i++) {
224                 memcpy(em_route_base_v6[i].v6_key.ip_dst, ipv6_l3fwd_em_route_array[i].key.ip_dst,
225                            sizeof(em_route_base_v6[i].v6_key.ip_dst));
226                 memcpy(em_route_base_v6[i].v6_key.ip_src, ipv6_l3fwd_em_route_array[i].key.ip_src,
227                            sizeof(em_route_base_v6[i].v6_key.ip_src));
228                 em_route_base_v6[i].v6_key.port_dst = ipv6_l3fwd_em_route_array[i].key.port_dst;
229                 em_route_base_v6[i].v6_key.port_src = ipv6_l3fwd_em_route_array[i].key.port_src;
230                 em_route_base_v6[i].v6_key.proto = ipv6_l3fwd_em_route_array[i].key.proto;
231                 em_route_base_v6[i].if_out = ipv6_l3fwd_em_route_array[i].if_out;
232         }
233         return 0;
234 }
235
236 void
237 em_free_routes(void)
238 {
239         free(em_route_base_v4);
240         free(em_route_base_v6);
241         em_route_base_v4 = NULL;
242         em_route_base_v6 = NULL;
243         route_num_v4 = 0;
244         route_num_v6 = 0;
245 }
246
247 /* Load rules from the input file */
248 void
249 read_config_files_em(void)
250 {
251         /* ipv4 check */
252         if (parm_config.rule_ipv4_name != NULL &&
253                         parm_config.rule_ipv6_name != NULL) {
254                 /* ipv4 check */
255                 route_num_v4 = em_add_rules(parm_config.rule_ipv4_name,
256                                         &em_route_base_v4, &em_parse_v4_rule);
257                 if (route_num_v4 < 0) {
258                         em_free_routes();
259                         rte_exit(EXIT_FAILURE, "Failed to add EM IPv4 rules\n");
260                 }
261
262                 /* ipv6 check */
263                 route_num_v6 = em_add_rules(parm_config.rule_ipv6_name,
264                                         &em_route_base_v6, &em_parse_v6_rule);
265                 if (route_num_v6 < 0) {
266                         em_free_routes();
267                         rte_exit(EXIT_FAILURE, "Failed to add EM IPv6 rules\n");
268                 }
269         } else {
270                 RTE_LOG(INFO, L3FWD, "Missing 1 or more rule files, using default instead\n");
271                 if (em_add_default_v4_rules() < 0) {
272                         em_free_routes();
273                         rte_exit(EXIT_FAILURE, "Failed to add default IPv4 rules\n");
274                 }
275                 if (em_add_default_v6_rules() < 0) {
276                         em_free_routes();
277                         rte_exit(EXIT_FAILURE, "Failed to add default IPv6 rules\n");
278                 }
279         }
280 }