1 /*- 2 * Copyright (c) 2011-2025 The NetBSD Foundation, Inc. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to The NetBSD Foundation 6 * by Christos Zoulas. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 /* 31 * NPF variables are used to build the intermediate representation (IR) 32 * of the configuration grammar. They represent primitive types (strings, 33 * numbers, etc) as well as complex types (address and mask, table, etc). 34 */ 35 36 #include <sys/cdefs.h> 37 __RCSID("$NetBSD: npf_var.c,v 1.16 2025/08/20 11:03:59 joe Exp $"); 38 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 43 #define _NPFVAR_PRIVATE 44 #include "npfctl.h" 45 46 typedef struct npf_element { 47 void * e_data; 48 unsigned e_type; 49 struct npf_element *e_next; 50 } npf_element_t; 51 52 struct npfvar { 53 char * v_key; 54 npf_element_t * v_elements; 55 npf_element_t * v_last; 56 size_t v_count; 57 void * v_next; 58 }; 59 60 static npfvar_t * var_list = NULL; 61 static size_t var_num = 0; 62 63 npfvar_t * 64 npfvar_create(void) 65 { 66 npfvar_t *vp = ecalloc(1, sizeof(*vp)); 67 var_num++; 68 return vp; 69 } 70 71 npfvar_t * 72 npfvar_lookup(const char *key) 73 { 74 for (npfvar_t *it = var_list; it != NULL; it = it->v_next) 75 if (strcmp(it->v_key, key) == 0) 76 return it; 77 return NULL; 78 } 79 80 const char * 81 npfvar_type(size_t t) 82 { 83 if (t >= __arraycount(npfvar_types)) { 84 return "unknown"; 85 } 86 return npfvar_types[t]; 87 } 88 89 void 90 npfvar_add(npfvar_t *vp, const char *name) 91 { 92 vp->v_key = estrdup(name); 93 vp->v_next = var_list; 94 var_list = vp; 95 } 96 97 npfvar_t * 98 npfvar_create_element(unsigned type, const void *data, size_t len) 99 { 100 npfvar_t *vp = npfvar_create(); 101 return npfvar_add_element(vp, type, data, len); 102 } 103 104 npfvar_t * 105 npfvar_create_from_string(unsigned type, const char *string) 106 { 107 return npfvar_create_element(type, string, strlen(string) + 1); 108 } 109 110 npfvar_t * 111 npfvar_add_element(npfvar_t *vp, unsigned type, const void *data, size_t len) 112 { 113 npf_element_t *el; 114 115 el = ecalloc(1, sizeof(*el)); 116 el->e_data = ecalloc(1, len); 117 el->e_type = type; 118 memcpy(el->e_data, data, len); 119 120 /* Preserve the order of insertion. */ 121 if (vp->v_elements == NULL) { 122 vp->v_elements = el; 123 } else { 124 vp->v_last->e_next = el; 125 } 126 vp->v_last = el; 127 vp->v_count++; 128 return vp; 129 } 130 131 npfvar_t * 132 npfvar_add_elements(npfvar_t *vp, npfvar_t *vp2) 133 { 134 if (vp2 == NULL) 135 return vp; 136 if (vp == NULL) 137 return vp2; 138 139 if (vp->v_elements == NULL) { 140 if (vp2->v_elements) { 141 vp->v_elements = vp2->v_elements; 142 } 143 } else if (vp2->v_elements) { 144 vp->v_last->e_next = vp2->v_elements; 145 } 146 if (vp2->v_elements) { 147 vp->v_last = vp2->v_last; 148 vp->v_count += vp2->v_count; 149 vp2->v_elements = NULL; 150 vp2->v_count = 0; 151 vp2->v_last = NULL; 152 } 153 npfvar_destroy(vp2); 154 return vp; 155 } 156 157 static void 158 npfvar_free_elements(npf_element_t *el) 159 { 160 if (el == NULL) 161 return; 162 npfvar_free_elements(el->e_next); 163 free(el->e_data); 164 free(el); 165 } 166 167 void 168 npfvar_destroy(npfvar_t *vp) 169 { 170 npfvar_free_elements(vp->v_elements); 171 free(vp->v_key); 172 free(vp); 173 var_num--; 174 } 175 176 char * 177 npfvar_expand_string(const npfvar_t *vp) 178 { 179 if (npfvar_get_count(vp) != 1) { 180 yyerror("variable '%s' has multiple elements", vp->v_key); 181 return NULL; 182 } 183 return npfvar_get_data(vp, NPFVAR_STRING, 0); 184 } 185 186 uint32_t 187 npfvar_expand_number(const npfvar_t *vp) 188 { 189 uint32_t *number; 190 if (npfvar_get_count(vp) != 1) { 191 yyerror("variable '%s' has multiple elements", vp->v_key); 192 } 193 number = (uint32_t *)npfvar_get_data(vp, NPFVAR_NUM, 0); 194 return *number; 195 } 196 197 void 198 npf_var_rid(char *var_id, rid_parser parser, uint32_t *rid, const char *ridt) 199 { 200 npfvar_t *vp = npfvar_lookup(var_id); 201 int type = npfvar_get_type(vp, 0); 202 char *rid_type; 203 204 switch (type) { 205 case NPFVAR_IDENTIFIER: 206 case NPFVAR_STRING: 207 rid_type = npfvar_expand_string(vp); 208 if (parser(rid_type, rid) == -1) { 209 yyerror("unknown %s %s", var_id, ridt); 210 } 211 break; 212 case NPFVAR_NUM: 213 *rid = npfvar_expand_number(vp); 214 break; 215 case -1: 216 yyerror("undefined variable '%s'", var_id); 217 break; 218 default: 219 yyerror("wrong variable '%s' type '%s' for %s id", 220 var_id, npfvar_type(type), ridt); 221 break; 222 } 223 } 224 225 size_t 226 npfvar_get_count(const npfvar_t *vp) 227 { 228 return vp ? vp->v_count : 0; 229 } 230 231 static npf_element_t * 232 npfvar_get_element(const npfvar_t *vp, size_t idx, size_t level) 233 { 234 npf_element_t *el; 235 236 /* 237 * Verify the parameters. 238 */ 239 if (vp == NULL) { 240 return NULL; 241 } 242 if (level >= var_num) { 243 yyerror("circular dependency for variable '%s'", vp->v_key); 244 return NULL; 245 } 246 if (vp->v_count <= idx) { 247 yyerror("variable '%s' has only %zu elements, requested %zu", 248 vp->v_key, vp->v_count, idx); 249 return NULL; 250 } 251 252 /* 253 * Get the element at the given index. 254 */ 255 el = vp->v_elements; 256 while (idx--) { 257 el = el->e_next; 258 } 259 260 return el; 261 } 262 263 /* 264 * now we can return a VAR_ID to be fully resolved 265 * and fleshed out in filter rules in both type and data 266 */ 267 void * 268 npfvar_getfilt_data(const npfvar_t *vp, unsigned type, size_t idx) 269 { 270 npf_element_t *el = npfvar_get_element(vp, idx, 0); 271 272 if (!el) 273 return NULL; 274 275 if (el && NPFVAR_TYPE(el->e_type) != NPFVAR_TYPE(type)) { 276 yyerror("variable '%s' element %zu " 277 "is of type '%s' rather than '%s'", vp->v_key, 278 idx, npfvar_type(el->e_type), npfvar_type(type)); 279 return NULL; 280 } 281 return el->e_data; 282 } 283 284 int 285 npfvar_getfilt_type(const npfvar_t *vp, size_t idx) 286 { 287 npf_element_t *el = npfvar_get_element(vp, idx, 0); 288 return el ? (int)el->e_type : -1; 289 } 290 291 int 292 npfvar_get_type(const npfvar_t *vp, size_t idx) 293 { 294 npf_element_t *el = npfvar_get_element(vp, idx, 0); 295 296 if (!el) 297 return -1; 298 299 /* 300 * Resolve if it is a reference to another variable. 301 */ 302 if (el->e_type == NPFVAR_VAR_ID) { 303 const npfvar_t *rvp = npfvar_lookup(el->e_data); 304 if (rvp == NULL) 305 yyerror("variable not found"); 306 307 return npfvar_get_type(rvp, idx); 308 } 309 return (int)el->e_type; 310 } 311 312 void * 313 npfvar_get_data(const npfvar_t *vp, unsigned type, size_t idx) 314 { 315 npf_element_t *el = npfvar_get_element(vp, idx, 0); 316 317 if (!el) 318 return NULL; 319 320 /* 321 * Resolve if it is a reference to another variable. 322 */ 323 if (el->e_type == NPFVAR_VAR_ID) { 324 const npfvar_t *rvp = npfvar_lookup(el->e_data); 325 if (rvp == NULL) 326 yyerror("variable not found"); 327 328 return npfvar_get_data(rvp, type, idx); 329 } 330 331 if (el && NPFVAR_TYPE(el->e_type) != NPFVAR_TYPE(type)) { 332 yyerror("variable '%s' element %zu " 333 "is of type '%s' rather than '%s'", vp->v_key, 334 idx, npfvar_type(el->e_type), npfvar_type(type)); 335 return NULL; 336 } 337 return el->e_data; 338 } 339