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