xref: /dpdk/examples/l3fwd/lpm_route_parse.c (revision e1a06e391ba74f9c4d46a6ecef6d8ee084f4229e)
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 enum {
14 	CB_FLD_DST_ADDR,
15 	CB_FLD_IF_OUT,
16 	CB_FLD_MAX
17 };
18 
19 struct lpm_route_rule *route_base_v4;
20 struct lpm_route_rule *route_base_v6;
21 int route_num_v4;
22 int route_num_v6;
23 
24 /* Bypass comment and empty lines */
25 int
26 is_bypass_line(const char *buff)
27 {
28 	int i = 0;
29 
30 	/* comment line */
31 	if (buff[0] == COMMENT_LEAD_CHAR)
32 		return 1;
33 	/* empty line */
34 	while (buff[i] != '\0') {
35 		if (!isspace(buff[i]))
36 			return 0;
37 		i++;
38 	}
39 	return 1;
40 }
41 
42 static int
43 parse_ipv6_addr_mask(char *token, struct rte_ipv6_addr *ipv6, uint8_t *mask)
44 {
45 	char *sa, *sm, *sv;
46 	const char *dlm =  "/";
47 
48 	sv = NULL;
49 	sa = strtok_r(token, dlm, &sv);
50 	if (sa == NULL)
51 		return -EINVAL;
52 	sm = strtok_r(NULL, dlm, &sv);
53 	if (sm == NULL)
54 		return -EINVAL;
55 
56 	if (inet_pton(AF_INET6, sa, ipv6) != 1)
57 		return -EINVAL;
58 
59 	GET_CB_FIELD(sm, *mask, 0, 128, 0);
60 	return 0;
61 }
62 
63 static int
64 parse_ipv4_addr_mask(char *token, uint32_t *ipv4, uint8_t *mask)
65 {
66 	char *sa, *sm, *sv;
67 	const char *dlm =  "/";
68 
69 	sv = NULL;
70 	sa = strtok_r(token, dlm, &sv);
71 	if (sa == NULL)
72 		return -EINVAL;
73 	sm = strtok_r(NULL, dlm, &sv);
74 	if (sm == NULL)
75 		return -EINVAL;
76 
77 	if (inet_pton(AF_INET, sa, ipv4) != 1)
78 		return -EINVAL;
79 
80 	GET_CB_FIELD(sm, *mask, 0, 32, 0);
81 	*ipv4 = ntohl(*ipv4);
82 	return 0;
83 }
84 
85 static int
86 lpm_parse_v6_net(char *in, struct rte_ipv6_addr *v, uint8_t *mask_len)
87 {
88 	int32_t rc;
89 
90 	/* get address. */
91 	rc = parse_ipv6_addr_mask(in, v, mask_len);
92 
93 	return rc;
94 }
95 
96 static int
97 lpm_parse_v6_rule(char *str, struct lpm_route_rule *v)
98 {
99 	int i, rc;
100 	char *s, *sp, *in[CB_FLD_MAX];
101 	static const char *dlm = " \t\n";
102 	int dim = CB_FLD_MAX;
103 	s = str;
104 
105 	for (i = 0; i != dim; i++, s = NULL) {
106 		in[i] = strtok_r(s, dlm, &sp);
107 		if (in[i] == NULL)
108 			return -EINVAL;
109 	}
110 
111 	rc = lpm_parse_v6_net(in[CB_FLD_DST_ADDR], &v->ip6, &v->depth);
112 
113 	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
114 
115 	return rc;
116 }
117 
118 static int
119 lpm_parse_v4_rule(char *str, struct lpm_route_rule *v)
120 {
121 	int i, rc;
122 	char *s, *sp, *in[CB_FLD_MAX];
123 	static const char *dlm = " \t\n";
124 	int dim = CB_FLD_MAX;
125 	s = str;
126 
127 	for (i = 0; i != dim; i++, s = NULL) {
128 		in[i] = strtok_r(s, dlm, &sp);
129 		if (in[i] == NULL)
130 			return -EINVAL;
131 	}
132 
133 	rc = parse_ipv4_addr_mask(in[CB_FLD_DST_ADDR], &v->ip, &v->depth);
134 
135 	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
136 
137 	return rc;
138 }
139 
140 static int
141 lpm_add_default_v4_rules(void)
142 {
143 	/* populate the LPM IPv4 table */
144 	unsigned int i, rule_size = sizeof(*route_base_v4);
145 	route_num_v4 = RTE_DIM(ipv4_l3fwd_route_array);
146 
147 	route_base_v4 = calloc(route_num_v4, rule_size);
148 
149 	for (i = 0; i < (unsigned int)route_num_v4; i++) {
150 		route_base_v4[i].ip = ipv4_l3fwd_route_array[i].ip;
151 		route_base_v4[i].depth = ipv4_l3fwd_route_array[i].depth;
152 		route_base_v4[i].if_out = ipv4_l3fwd_route_array[i].if_out;
153 	}
154 	return 0;
155 }
156 
157 static int
158 lpm_add_default_v6_rules(void)
159 {
160 	/* populate the LPM IPv6 table */
161 	unsigned int i, rule_size = sizeof(*route_base_v6);
162 	route_num_v6 = RTE_DIM(ipv6_l3fwd_route_array);
163 
164 	route_base_v6 = calloc(route_num_v6, rule_size);
165 
166 	for (i = 0; i < (unsigned int)route_num_v6; i++) {
167 		route_base_v6[i].ip6 = ipv6_l3fwd_route_array[i].ip;
168 		route_base_v6[i].depth = ipv6_l3fwd_route_array[i].depth;
169 		route_base_v6[i].if_out = ipv6_l3fwd_route_array[i].if_out;
170 	}
171 	return 0;
172 }
173 
174 static int
175 lpm_add_rules(const char *rule_path,
176 		struct lpm_route_rule **proute_base,
177 		int (*parser)(char *, struct lpm_route_rule *))
178 {
179 	struct lpm_route_rule *route_rules;
180 	struct lpm_route_rule *next;
181 	unsigned int route_num = 0;
182 	unsigned int route_cnt = 0;
183 	char buff[LINE_MAX];
184 	FILE *fh;
185 	unsigned int i = 0, rule_size = sizeof(*next);
186 	int val;
187 
188 	*proute_base = NULL;
189 	fh = fopen(rule_path, "rb");
190 	if (fh == NULL)
191 		return -EINVAL;
192 
193 	while ((fgets(buff, LINE_MAX, fh) != NULL)) {
194 		if (buff[0] == ROUTE_LEAD_CHAR)
195 			route_num++;
196 	}
197 
198 	if (route_num == 0) {
199 		fclose(fh);
200 		return -EINVAL;
201 	}
202 
203 	val = fseek(fh, 0, SEEK_SET);
204 	if (val < 0) {
205 		fclose(fh);
206 		return -EINVAL;
207 	}
208 
209 	route_rules = calloc(route_num, rule_size);
210 
211 	if (route_rules == NULL) {
212 		fclose(fh);
213 		return -EINVAL;
214 	}
215 
216 	i = 0;
217 	while (fgets(buff, LINE_MAX, fh) != NULL) {
218 		i++;
219 		if (is_bypass_line(buff))
220 			continue;
221 
222 		char s = buff[0];
223 
224 		/* Route entry */
225 		if (s == ROUTE_LEAD_CHAR)
226 			next = &route_rules[route_cnt];
227 
228 		/* Illegal line */
229 		else {
230 			RTE_LOG(ERR, L3FWD,
231 				"%s Line %u: should start with leading "
232 				"char %c\n",
233 				rule_path, i, ROUTE_LEAD_CHAR);
234 			fclose(fh);
235 			free(route_rules);
236 			return -EINVAL;
237 		}
238 
239 		if (parser(buff + 1, next) != 0) {
240 			RTE_LOG(ERR, L3FWD,
241 				"%s Line %u: parse rules error\n",
242 				rule_path, i);
243 			fclose(fh);
244 			free(route_rules);
245 			return -EINVAL;
246 		}
247 
248 		route_cnt++;
249 	}
250 
251 	fclose(fh);
252 
253 	*proute_base = route_rules;
254 
255 	return route_cnt;
256 }
257 
258 void
259 lpm_free_routes(void)
260 {
261 	free(route_base_v4);
262 	free(route_base_v6);
263 	route_base_v4 = NULL;
264 	route_base_v6 = NULL;
265 	route_num_v4 = 0;
266 	route_num_v6 = 0;
267 }
268 
269 /* Load rules from the input file */
270 void
271 read_config_files_lpm(void)
272 {
273 	if (parm_config.rule_ipv4_name != NULL &&
274 			parm_config.rule_ipv6_name != NULL) {
275 		/* ipv4 check */
276 		route_num_v4 = lpm_add_rules(parm_config.rule_ipv4_name,
277 					&route_base_v4, &lpm_parse_v4_rule);
278 		if (route_num_v4 < 0) {
279 			lpm_free_routes();
280 			rte_exit(EXIT_FAILURE, "Failed to add IPv4 rules\n");
281 		}
282 
283 		/* ipv6 check */
284 		route_num_v6 = lpm_add_rules(parm_config.rule_ipv6_name,
285 					&route_base_v6, &lpm_parse_v6_rule);
286 		if (route_num_v6 < 0) {
287 			lpm_free_routes();
288 			rte_exit(EXIT_FAILURE, "Failed to add IPv6 rules\n");
289 		}
290 	} else {
291 		RTE_LOG(INFO, L3FWD, "Missing 1 or more rule files, using default instead\n");
292 		if (lpm_add_default_v4_rules() < 0) {
293 			lpm_free_routes();
294 			rte_exit(EXIT_FAILURE, "Failed to add default IPv4 rules\n");
295 		}
296 		if (lpm_add_default_v6_rules() < 0) {
297 			lpm_free_routes();
298 			rte_exit(EXIT_FAILURE, "Failed to add default IPv6 rules\n");
299 		}
300 	}
301 }
302