xref: /dpdk/examples/l3fwd/lpm_route_parse.c (revision 97b914f4e715565d53d38ac6e04815b9be5e58a9)
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, uint32_t *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, uint32_t *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->ip_32, &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 		memcpy(route_base_v6[i].ip_8, ipv6_l3fwd_route_array[i].ip,
168 			   sizeof(route_base_v6[i].ip_8));
169 		route_base_v6[i].depth = ipv6_l3fwd_route_array[i].depth;
170 		route_base_v6[i].if_out = ipv6_l3fwd_route_array[i].if_out;
171 	}
172 	return 0;
173 }
174 
175 static int
176 lpm_add_rules(const char *rule_path,
177 		struct lpm_route_rule **proute_base,
178 		int (*parser)(char *, struct lpm_route_rule *))
179 {
180 	struct lpm_route_rule *route_rules;
181 	struct lpm_route_rule *next;
182 	unsigned int route_num = 0;
183 	unsigned int route_cnt = 0;
184 	char buff[LINE_MAX];
185 	FILE *fh;
186 	unsigned int i = 0, rule_size = sizeof(*next);
187 	int val;
188 
189 	*proute_base = NULL;
190 	fh = fopen(rule_path, "rb");
191 	if (fh == NULL)
192 		return -EINVAL;
193 
194 	while ((fgets(buff, LINE_MAX, fh) != NULL)) {
195 		if (buff[0] == ROUTE_LEAD_CHAR)
196 			route_num++;
197 	}
198 
199 	if (route_num == 0) {
200 		fclose(fh);
201 		return -EINVAL;
202 	}
203 
204 	val = fseek(fh, 0, SEEK_SET);
205 	if (val < 0) {
206 		fclose(fh);
207 		return -EINVAL;
208 	}
209 
210 	route_rules = calloc(route_num, rule_size);
211 
212 	if (route_rules == NULL) {
213 		fclose(fh);
214 		return -EINVAL;
215 	}
216 
217 	i = 0;
218 	while (fgets(buff, LINE_MAX, fh) != NULL) {
219 		i++;
220 		if (is_bypass_line(buff))
221 			continue;
222 
223 		char s = buff[0];
224 
225 		/* Route entry */
226 		if (s == ROUTE_LEAD_CHAR)
227 			next = &route_rules[route_cnt];
228 
229 		/* Illegal line */
230 		else {
231 			RTE_LOG(ERR, L3FWD,
232 				"%s Line %u: should start with leading "
233 				"char %c\n",
234 				rule_path, i, ROUTE_LEAD_CHAR);
235 			fclose(fh);
236 			free(route_rules);
237 			return -EINVAL;
238 		}
239 
240 		if (parser(buff + 1, next) != 0) {
241 			RTE_LOG(ERR, L3FWD,
242 				"%s Line %u: parse rules error\n",
243 				rule_path, i);
244 			fclose(fh);
245 			free(route_rules);
246 			return -EINVAL;
247 		}
248 
249 		route_cnt++;
250 	}
251 
252 	fclose(fh);
253 
254 	*proute_base = route_rules;
255 
256 	return route_cnt;
257 }
258 
259 void
260 lpm_free_routes(void)
261 {
262 	free(route_base_v4);
263 	free(route_base_v6);
264 	route_base_v4 = NULL;
265 	route_base_v6 = NULL;
266 	route_num_v4 = 0;
267 	route_num_v6 = 0;
268 }
269 
270 /* Load rules from the input file */
271 void
272 read_config_files_lpm(void)
273 {
274 	if (parm_config.rule_ipv4_name != NULL &&
275 			parm_config.rule_ipv6_name != NULL) {
276 		/* ipv4 check */
277 		route_num_v4 = lpm_add_rules(parm_config.rule_ipv4_name,
278 					&route_base_v4, &lpm_parse_v4_rule);
279 		if (route_num_v4 < 0) {
280 			lpm_free_routes();
281 			rte_exit(EXIT_FAILURE, "Failed to add IPv4 rules\n");
282 		}
283 
284 		/* ipv6 check */
285 		route_num_v6 = lpm_add_rules(parm_config.rule_ipv6_name,
286 					&route_base_v6, &lpm_parse_v6_rule);
287 		if (route_num_v6 < 0) {
288 			lpm_free_routes();
289 			rte_exit(EXIT_FAILURE, "Failed to add IPv6 rules\n");
290 		}
291 	} else {
292 		RTE_LOG(INFO, L3FWD, "Missing 1 or more rule files, using default instead\n");
293 		if (lpm_add_default_v4_rules() < 0) {
294 			lpm_free_routes();
295 			rte_exit(EXIT_FAILURE, "Failed to add default IPv4 rules\n");
296 		}
297 		if (lpm_add_default_v6_rules() < 0) {
298 			lpm_free_routes();
299 			rte_exit(EXIT_FAILURE, "Failed to add default IPv6 rules\n");
300 		}
301 	}
302 }
303