152def963SSean Morrissey /* SPDX-License-Identifier: BSD-3-Clause 252def963SSean Morrissey * Copyright(c) 2022 Intel Corporation 352def963SSean Morrissey */ 452def963SSean Morrissey 552def963SSean Morrissey #include <stdio.h> 652def963SSean Morrissey #include <stdint.h> 752def963SSean Morrissey #include <errno.h> 852def963SSean Morrissey #include <sys/socket.h> 952def963SSean Morrissey 1052def963SSean Morrissey #include "l3fwd.h" 1152def963SSean Morrissey #include "l3fwd_route.h" 1252def963SSean Morrissey 1352def963SSean Morrissey enum { 1452def963SSean Morrissey CB_FLD_DST_ADDR, 1552def963SSean Morrissey CB_FLD_IF_OUT, 1652def963SSean Morrissey CB_FLD_MAX 1752def963SSean Morrissey }; 1852def963SSean Morrissey 1952def963SSean Morrissey struct lpm_route_rule *route_base_v4; 2052def963SSean Morrissey struct lpm_route_rule *route_base_v6; 2152def963SSean Morrissey int route_num_v4; 2252def963SSean Morrissey int route_num_v6; 2352def963SSean Morrissey 2452def963SSean Morrissey /* Bypass comment and empty lines */ 2552def963SSean Morrissey int 2652def963SSean Morrissey is_bypass_line(const char *buff) 2752def963SSean Morrissey { 2852def963SSean Morrissey int i = 0; 2952def963SSean Morrissey 3052def963SSean Morrissey /* comment line */ 3152def963SSean Morrissey if (buff[0] == COMMENT_LEAD_CHAR) 3252def963SSean Morrissey return 1; 3352def963SSean Morrissey /* empty line */ 3452def963SSean Morrissey while (buff[i] != '\0') { 3552def963SSean Morrissey if (!isspace(buff[i])) 3652def963SSean Morrissey return 0; 3752def963SSean Morrissey i++; 3852def963SSean Morrissey } 3952def963SSean Morrissey return 1; 4052def963SSean Morrissey } 4152def963SSean Morrissey 4252def963SSean Morrissey static int 43*e1a06e39SRobin Jarry parse_ipv6_addr_mask(char *token, struct rte_ipv6_addr *ipv6, uint8_t *mask) 4452def963SSean Morrissey { 4552def963SSean Morrissey char *sa, *sm, *sv; 4652def963SSean Morrissey const char *dlm = "/"; 4752def963SSean Morrissey 4852def963SSean Morrissey sv = NULL; 4952def963SSean Morrissey sa = strtok_r(token, dlm, &sv); 5052def963SSean Morrissey if (sa == NULL) 5152def963SSean Morrissey return -EINVAL; 5252def963SSean Morrissey sm = strtok_r(NULL, dlm, &sv); 5352def963SSean Morrissey if (sm == NULL) 5452def963SSean Morrissey return -EINVAL; 5552def963SSean Morrissey 5652def963SSean Morrissey if (inet_pton(AF_INET6, sa, ipv6) != 1) 5752def963SSean Morrissey return -EINVAL; 5852def963SSean Morrissey 5952def963SSean Morrissey GET_CB_FIELD(sm, *mask, 0, 128, 0); 6052def963SSean Morrissey return 0; 6152def963SSean Morrissey } 6252def963SSean Morrissey 6352def963SSean Morrissey static int 6452def963SSean Morrissey parse_ipv4_addr_mask(char *token, uint32_t *ipv4, uint8_t *mask) 6552def963SSean Morrissey { 6652def963SSean Morrissey char *sa, *sm, *sv; 6752def963SSean Morrissey const char *dlm = "/"; 6852def963SSean Morrissey 6952def963SSean Morrissey sv = NULL; 7052def963SSean Morrissey sa = strtok_r(token, dlm, &sv); 7152def963SSean Morrissey if (sa == NULL) 7252def963SSean Morrissey return -EINVAL; 7352def963SSean Morrissey sm = strtok_r(NULL, dlm, &sv); 7452def963SSean Morrissey if (sm == NULL) 7552def963SSean Morrissey return -EINVAL; 7652def963SSean Morrissey 7752def963SSean Morrissey if (inet_pton(AF_INET, sa, ipv4) != 1) 7852def963SSean Morrissey return -EINVAL; 7952def963SSean Morrissey 8052def963SSean Morrissey GET_CB_FIELD(sm, *mask, 0, 32, 0); 8152def963SSean Morrissey *ipv4 = ntohl(*ipv4); 8252def963SSean Morrissey return 0; 8352def963SSean Morrissey } 8452def963SSean Morrissey 8552def963SSean Morrissey static int 86*e1a06e39SRobin Jarry lpm_parse_v6_net(char *in, struct rte_ipv6_addr *v, uint8_t *mask_len) 8752def963SSean Morrissey { 8852def963SSean Morrissey int32_t rc; 8952def963SSean Morrissey 9052def963SSean Morrissey /* get address. */ 9152def963SSean Morrissey rc = parse_ipv6_addr_mask(in, v, mask_len); 9252def963SSean Morrissey 9352def963SSean Morrissey return rc; 9452def963SSean Morrissey } 9552def963SSean Morrissey 9652def963SSean Morrissey static int 9752def963SSean Morrissey lpm_parse_v6_rule(char *str, struct lpm_route_rule *v) 9852def963SSean Morrissey { 9952def963SSean Morrissey int i, rc; 10052def963SSean Morrissey char *s, *sp, *in[CB_FLD_MAX]; 10152def963SSean Morrissey static const char *dlm = " \t\n"; 10252def963SSean Morrissey int dim = CB_FLD_MAX; 10352def963SSean Morrissey s = str; 10452def963SSean Morrissey 10552def963SSean Morrissey for (i = 0; i != dim; i++, s = NULL) { 10652def963SSean Morrissey in[i] = strtok_r(s, dlm, &sp); 10752def963SSean Morrissey if (in[i] == NULL) 10852def963SSean Morrissey return -EINVAL; 10952def963SSean Morrissey } 11052def963SSean Morrissey 111*e1a06e39SRobin Jarry rc = lpm_parse_v6_net(in[CB_FLD_DST_ADDR], &v->ip6, &v->depth); 11252def963SSean Morrissey 11352def963SSean Morrissey GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0); 11452def963SSean Morrissey 11552def963SSean Morrissey return rc; 11652def963SSean Morrissey } 11752def963SSean Morrissey 11852def963SSean Morrissey static int 11952def963SSean Morrissey lpm_parse_v4_rule(char *str, struct lpm_route_rule *v) 12052def963SSean Morrissey { 12152def963SSean Morrissey int i, rc; 12252def963SSean Morrissey char *s, *sp, *in[CB_FLD_MAX]; 12352def963SSean Morrissey static const char *dlm = " \t\n"; 12452def963SSean Morrissey int dim = CB_FLD_MAX; 12552def963SSean Morrissey s = str; 12652def963SSean Morrissey 12752def963SSean Morrissey for (i = 0; i != dim; i++, s = NULL) { 12852def963SSean Morrissey in[i] = strtok_r(s, dlm, &sp); 12952def963SSean Morrissey if (in[i] == NULL) 13052def963SSean Morrissey return -EINVAL; 13152def963SSean Morrissey } 13252def963SSean Morrissey 13352def963SSean Morrissey rc = parse_ipv4_addr_mask(in[CB_FLD_DST_ADDR], &v->ip, &v->depth); 13452def963SSean Morrissey 13552def963SSean Morrissey GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0); 13652def963SSean Morrissey 13752def963SSean Morrissey return rc; 13852def963SSean Morrissey } 13952def963SSean Morrissey 14052def963SSean Morrissey static int 14152def963SSean Morrissey lpm_add_default_v4_rules(void) 14252def963SSean Morrissey { 14352def963SSean Morrissey /* populate the LPM IPv4 table */ 14452def963SSean Morrissey unsigned int i, rule_size = sizeof(*route_base_v4); 14552def963SSean Morrissey route_num_v4 = RTE_DIM(ipv4_l3fwd_route_array); 14652def963SSean Morrissey 14752def963SSean Morrissey route_base_v4 = calloc(route_num_v4, rule_size); 14852def963SSean Morrissey 14952def963SSean Morrissey for (i = 0; i < (unsigned int)route_num_v4; i++) { 15052def963SSean Morrissey route_base_v4[i].ip = ipv4_l3fwd_route_array[i].ip; 15152def963SSean Morrissey route_base_v4[i].depth = ipv4_l3fwd_route_array[i].depth; 15252def963SSean Morrissey route_base_v4[i].if_out = ipv4_l3fwd_route_array[i].if_out; 15352def963SSean Morrissey } 15452def963SSean Morrissey return 0; 15552def963SSean Morrissey } 15652def963SSean Morrissey 15752def963SSean Morrissey static int 15852def963SSean Morrissey lpm_add_default_v6_rules(void) 15952def963SSean Morrissey { 16052def963SSean Morrissey /* populate the LPM IPv6 table */ 16152def963SSean Morrissey unsigned int i, rule_size = sizeof(*route_base_v6); 16252def963SSean Morrissey route_num_v6 = RTE_DIM(ipv6_l3fwd_route_array); 16352def963SSean Morrissey 16452def963SSean Morrissey route_base_v6 = calloc(route_num_v6, rule_size); 16552def963SSean Morrissey 16652def963SSean Morrissey for (i = 0; i < (unsigned int)route_num_v6; i++) { 167*e1a06e39SRobin Jarry route_base_v6[i].ip6 = ipv6_l3fwd_route_array[i].ip; 16852def963SSean Morrissey route_base_v6[i].depth = ipv6_l3fwd_route_array[i].depth; 16952def963SSean Morrissey route_base_v6[i].if_out = ipv6_l3fwd_route_array[i].if_out; 17052def963SSean Morrissey } 17152def963SSean Morrissey return 0; 17252def963SSean Morrissey } 17352def963SSean Morrissey 17452def963SSean Morrissey static int 17552def963SSean Morrissey lpm_add_rules(const char *rule_path, 17652def963SSean Morrissey struct lpm_route_rule **proute_base, 17752def963SSean Morrissey int (*parser)(char *, struct lpm_route_rule *)) 17852def963SSean Morrissey { 17952def963SSean Morrissey struct lpm_route_rule *route_rules; 18052def963SSean Morrissey struct lpm_route_rule *next; 18152def963SSean Morrissey unsigned int route_num = 0; 18252def963SSean Morrissey unsigned int route_cnt = 0; 18352def963SSean Morrissey char buff[LINE_MAX]; 18452def963SSean Morrissey FILE *fh; 18552def963SSean Morrissey unsigned int i = 0, rule_size = sizeof(*next); 18652def963SSean Morrissey int val; 18752def963SSean Morrissey 18852def963SSean Morrissey *proute_base = NULL; 18952def963SSean Morrissey fh = fopen(rule_path, "rb"); 19052def963SSean Morrissey if (fh == NULL) 19152def963SSean Morrissey return -EINVAL; 19252def963SSean Morrissey 19352def963SSean Morrissey while ((fgets(buff, LINE_MAX, fh) != NULL)) { 19452def963SSean Morrissey if (buff[0] == ROUTE_LEAD_CHAR) 19552def963SSean Morrissey route_num++; 19652def963SSean Morrissey } 19752def963SSean Morrissey 19852def963SSean Morrissey if (route_num == 0) { 19952def963SSean Morrissey fclose(fh); 20052def963SSean Morrissey return -EINVAL; 20152def963SSean Morrissey } 20252def963SSean Morrissey 20352def963SSean Morrissey val = fseek(fh, 0, SEEK_SET); 20452def963SSean Morrissey if (val < 0) { 20552def963SSean Morrissey fclose(fh); 20652def963SSean Morrissey return -EINVAL; 20752def963SSean Morrissey } 20852def963SSean Morrissey 20952def963SSean Morrissey route_rules = calloc(route_num, rule_size); 21052def963SSean Morrissey 21152def963SSean Morrissey if (route_rules == NULL) { 21252def963SSean Morrissey fclose(fh); 21352def963SSean Morrissey return -EINVAL; 21452def963SSean Morrissey } 21552def963SSean Morrissey 21652def963SSean Morrissey i = 0; 21752def963SSean Morrissey while (fgets(buff, LINE_MAX, fh) != NULL) { 21852def963SSean Morrissey i++; 21952def963SSean Morrissey if (is_bypass_line(buff)) 22052def963SSean Morrissey continue; 22152def963SSean Morrissey 22252def963SSean Morrissey char s = buff[0]; 22352def963SSean Morrissey 22452def963SSean Morrissey /* Route entry */ 22552def963SSean Morrissey if (s == ROUTE_LEAD_CHAR) 22652def963SSean Morrissey next = &route_rules[route_cnt]; 22752def963SSean Morrissey 22852def963SSean Morrissey /* Illegal line */ 22952def963SSean Morrissey else { 23052def963SSean Morrissey RTE_LOG(ERR, L3FWD, 23152def963SSean Morrissey "%s Line %u: should start with leading " 23252def963SSean Morrissey "char %c\n", 23352def963SSean Morrissey rule_path, i, ROUTE_LEAD_CHAR); 23452def963SSean Morrissey fclose(fh); 23552def963SSean Morrissey free(route_rules); 23652def963SSean Morrissey return -EINVAL; 23752def963SSean Morrissey } 23852def963SSean Morrissey 23952def963SSean Morrissey if (parser(buff + 1, next) != 0) { 24052def963SSean Morrissey RTE_LOG(ERR, L3FWD, 24152def963SSean Morrissey "%s Line %u: parse rules error\n", 24252def963SSean Morrissey rule_path, i); 24352def963SSean Morrissey fclose(fh); 24452def963SSean Morrissey free(route_rules); 24552def963SSean Morrissey return -EINVAL; 24652def963SSean Morrissey } 24752def963SSean Morrissey 24852def963SSean Morrissey route_cnt++; 24952def963SSean Morrissey } 25052def963SSean Morrissey 25152def963SSean Morrissey fclose(fh); 25252def963SSean Morrissey 25352def963SSean Morrissey *proute_base = route_rules; 25452def963SSean Morrissey 25552def963SSean Morrissey return route_cnt; 25652def963SSean Morrissey } 25752def963SSean Morrissey 25852def963SSean Morrissey void 25952def963SSean Morrissey lpm_free_routes(void) 26052def963SSean Morrissey { 26152def963SSean Morrissey free(route_base_v4); 26252def963SSean Morrissey free(route_base_v6); 26352def963SSean Morrissey route_base_v4 = NULL; 26452def963SSean Morrissey route_base_v6 = NULL; 26552def963SSean Morrissey route_num_v4 = 0; 26652def963SSean Morrissey route_num_v6 = 0; 26752def963SSean Morrissey } 26852def963SSean Morrissey 26952def963SSean Morrissey /* Load rules from the input file */ 27052def963SSean Morrissey void 27152def963SSean Morrissey read_config_files_lpm(void) 27252def963SSean Morrissey { 27352def963SSean Morrissey if (parm_config.rule_ipv4_name != NULL && 27452def963SSean Morrissey parm_config.rule_ipv6_name != NULL) { 27552def963SSean Morrissey /* ipv4 check */ 27652def963SSean Morrissey route_num_v4 = lpm_add_rules(parm_config.rule_ipv4_name, 27752def963SSean Morrissey &route_base_v4, &lpm_parse_v4_rule); 27852def963SSean Morrissey if (route_num_v4 < 0) { 27952def963SSean Morrissey lpm_free_routes(); 28052def963SSean Morrissey rte_exit(EXIT_FAILURE, "Failed to add IPv4 rules\n"); 28152def963SSean Morrissey } 28252def963SSean Morrissey 28352def963SSean Morrissey /* ipv6 check */ 28452def963SSean Morrissey route_num_v6 = lpm_add_rules(parm_config.rule_ipv6_name, 28552def963SSean Morrissey &route_base_v6, &lpm_parse_v6_rule); 28652def963SSean Morrissey if (route_num_v6 < 0) { 28752def963SSean Morrissey lpm_free_routes(); 28852def963SSean Morrissey rte_exit(EXIT_FAILURE, "Failed to add IPv6 rules\n"); 28952def963SSean Morrissey } 29052def963SSean Morrissey } else { 29152def963SSean Morrissey RTE_LOG(INFO, L3FWD, "Missing 1 or more rule files, using default instead\n"); 29252def963SSean Morrissey if (lpm_add_default_v4_rules() < 0) { 29352def963SSean Morrissey lpm_free_routes(); 29452def963SSean Morrissey rte_exit(EXIT_FAILURE, "Failed to add default IPv4 rules\n"); 29552def963SSean Morrissey } 29652def963SSean Morrissey if (lpm_add_default_v6_rules() < 0) { 29752def963SSean Morrissey lpm_free_routes(); 29852def963SSean Morrissey rte_exit(EXIT_FAILURE, "Failed to add default IPv6 rules\n"); 29952def963SSean Morrissey } 30052def963SSean Morrissey } 30152def963SSean Morrissey } 302