1/* $NetBSD: parse.y$ */ 2 3/*- 4 * Copyright (c) 2010 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29%{ 30#include <sys/cdefs.h> 31 32#ifndef lint 33__RCSID("$NetBSD: parse.y$"); 34#endif 35 36#include <stdio.h> 37#include <stdlib.h> 38#include <string.h> 39#include <stdint.h> 40#include <stdbool.h> 41#include <inttypes.h> 42#include <errno.h> 43 44#include <net/if.h> 45#include <netinet/in.h> 46#include <net/pfvar.h> 47#include <arpa/inet.h> 48#include <netdb.h> 49#include <netinet/tcp_fsm.h> 50 51#include "parser.h" 52 53// XXX it is really correct ? 54extern const char * const tcpstates[]; 55 56 57struct pfsync_state global_state; 58struct pfsync_state_peer *src_peer, *dst_peer; 59struct pfsync_state_peer current_peer; 60 61static void parse_init(void); 62static void add_state(void); 63static bool get_pfsync_host(const char*, struct pfsync_state_host*, sa_family_t*); 64static uint8_t retrieve_peer_state(const char*, int); 65static bool retrieve_seq(const char*, struct pfsync_state_peer*); 66static bool strtou32(const char*, uint32_t*); 67 68%} 69 70%union { 71 uintmax_t num; 72 char* str; 73} 74 75%token STATE 76%token IN OUT 77%token ON PROTO 78%token FROM TO USING 79%token ID CID EXPIRE TIMEOUT 80%token SRC DST 81%token SEQ MAX_WIN WSCALE MSS 82%token NOSCRUB SCRUB FLAGS TTL MODE 83%token NUMBER STRING 84 85%type <str> STRING 86%type <num> NUMBER 87%% 88 89states 90 : /* NOTHING */ 91 | state states { parse_init(); } 92 ; 93 94state 95 : STATE direction iface proto addrs id cid expire timeout src_peer dst_peer { 96 add_state(); 97 } 98 ; 99 100direction 101 : IN { 102 global_state.direction = PF_IN; 103 src_peer = &global_state.dst; 104 dst_peer = &global_state.src; 105 } 106 | OUT { 107 global_state.direction = PF_OUT; 108 src_peer = &global_state.src; 109 dst_peer = &global_state.dst; 110 } 111 ; 112 113iface 114 : ON STRING { 115 strlcpy(global_state.ifname, $2, sizeof(global_state.ifname)); 116 free($2); 117 } 118 ; 119 120proto 121 : PROTO STRING { 122 struct protoent *p; 123 p = getprotobyname($2); 124 if (p == NULL) 125 yyfatal("Invalid protocol name"); 126 global_state.proto = p->p_proto; 127 free($2); 128 } 129 | PROTO NUMBER { 130 // check that the number may be valid proto ? 131 global_state.proto = $2; 132 } 133 ; 134 135addrs 136 : FROM STRING TO STRING { 137 get_pfsync_host($2, &global_state.lan, &global_state.af); 138 get_pfsync_host($4, &global_state.ext, &global_state.af); 139 memcpy(&global_state.gwy, &global_state.lan, sizeof(struct pfsync_state_host)); 140 free($2); 141 free($4); 142 } 143 | FROM STRING TO STRING USING STRING { 144 get_pfsync_host($2, &global_state.lan, &global_state.af); 145 get_pfsync_host($4, &global_state.ext, &global_state.af); 146 get_pfsync_host($6, &global_state.gwy, &global_state.af); 147 free($2); 148 free($4); 149 free($6); 150 } 151 ; 152 153id 154 : ID NUMBER { 155 if ( $2 > UINT64_MAX) 156 yyfatal("id is too big"); 157 uint64_t value = (uint64_t)$2; 158 memcpy(global_state.id, &value, sizeof(global_state.id)); 159 } 160 ; 161 162cid 163 : CID NUMBER { 164 if ( $2 > UINT32_MAX) 165 yyfatal("creator id is too big"); 166 global_state.creatorid = (uint32_t)$2; 167 } 168 ; 169 170expire 171 : EXPIRE NUMBER { 172 if ( $2 > UINT32_MAX) 173 yyfatal("expire time is too big"); 174 global_state.expire = (uint32_t) $2; 175 } 176 ; 177 178timeout 179 : TIMEOUT NUMBER { 180 if ($2 > UINT8_MAX) 181 yyfatal("timeout time is too big"); 182 global_state.timeout = (uint8_t) $2; 183 } 184 ; 185 186src_peer 187 : SRC peer { 188 memcpy(src_peer, ¤t_peer, sizeof(current_peer)); 189 } 190 ; 191 192dst_peer 193 : DST peer { 194 memcpy(dst_peer, ¤t_peer, sizeof(current_peer)); 195 } 196 ; 197 198peer 199 : peer_state scrub 200 | peer_state tcp_options scrub 201 ; 202 203peer_state 204 : STATE STRING { 205 current_peer.state = retrieve_peer_state($2, global_state.proto); 206 free($2); 207 } 208 | STATE NUMBER { 209 if ( $2 > UINT8_MAX) 210 yyfatal("peer state is too big"); 211 current_peer.state = $2; 212 } 213 ; 214 215tcp_options 216 : SEQ seqs MAX_WIN NUMBER WSCALE NUMBER { 217 if ($4 > UINT16_MAX) 218 yyfatal("max_win is too big"); 219 current_peer.max_win = $4; 220 221 if ($6 > UINT8_MAX) 222 yyfatal("wscale is too big"); 223 current_peer.wscale = $6; 224 } 225 | SEQ seqs MAX_WIN NUMBER WSCALE NUMBER MSS NUMBER { 226 if ($4 > UINT16_MAX) 227 yyfatal("max_win is too big"); 228 current_peer.max_win = $4; 229 230 if ($6 > UINT8_MAX) 231 yyfatal("wscale is too big"); 232 current_peer.wscale = $6; 233 234 if ($8 > UINT16_MAX) 235 yyfatal("mss is too big"); 236 current_peer.mss = $8; 237 } 238 ; 239 240seqs 241 : STRING { 242 if (!retrieve_seq($1, ¤t_peer)) 243 yyfatal("invalid seq number"); 244 245 free($1); 246 } 247 ; 248 249scrub 250 : NOSCRUB { current_peer.scrub.scrub_flag= 0;} 251 | SCRUB FLAGS NUMBER MODE NUMBER TTL NUMBER { 252 current_peer.scrub.scrub_flag= PFSYNC_SCRUB_FLAG_VALID; 253 if ($3 > UINT16_MAX) 254 yyfatal("scrub flags is too big"); 255 current_peer.scrub.pfss_flags = $3; 256 257 if ($5 > UINT32_MAX) 258 yyfatal("scrub mode is too big"); 259 current_peer.scrub.pfss_ts_mod = $5; 260 261 if ($7 > UINT8_MAX) 262 yyfatal("scrub ttl is too big"); 263 current_peer.scrub.pfss_ttl = $7; 264 } 265 ; 266 267 268%% 269 270static void 271parse_init(void) 272{ 273 memset(&global_state, 0, sizeof(global_state)); 274 memset(¤t_peer, 0, sizeof(current_peer)); 275 src_peer = NULL; 276 dst_peer = NULL; 277} 278 279static bool 280get_pfsync_host(const char* str, struct pfsync_state_host* host, sa_family_t* af) 281{ 282 size_t count_colon, addr_len, port_len; 283 const char* p, *last_colon, *first_bracket, *last_bracket; 284 char buf[48]; 285 char buf_port[6]; 286 287 if (str == NULL || *str == '\0') 288 return false; 289 290 p = str; 291 last_colon = NULL; 292 count_colon = 0; 293 294 while (*p != '\0') { 295 if (*p == ':') { 296 count_colon++; 297 last_colon = p; 298 } 299 p++; 300 } 301 302 /* 303 * If no colon, it is not an expected addr 304 * If there are more than one colon, we guess that af = AF_INET6 305 */ 306 307 if (count_colon == 0) 308 return false; 309 310 if (count_colon == 1) 311 *af = AF_INET; 312 else 313 *af = AF_INET6; 314 315 /* 316 * First bracket must be next character after last colon 317 * Last bracket must be the last character 318 * distance between both must be <= 7 319 */ 320 321 if (*(last_colon+1) == '[') 322 first_bracket = last_colon + 1; 323 else 324 return false; 325 326 last_bracket = str + (strlen(str) - 1); 327 if (*last_bracket != ']') 328 return false; 329 330 port_len = last_bracket - first_bracket; 331 if (last_bracket - first_bracket > 7) 332 return false; 333 334 memcpy(buf_port, first_bracket +1, port_len - 1); 335 buf_port[port_len-1]= '\0'; 336 337 addr_len = last_colon - str; 338 if (addr_len >= sizeof(buf)) 339 return false; 340 memcpy(buf, str, addr_len); 341 buf[addr_len] = '\0'; 342 343 if (inet_pton(*af, buf, &host->addr) != 1) 344 return false; 345 346 host->port = htons(atoi(buf_port)); 347 348 return true; 349} 350 351static uint8_t 352retrieve_peer_state(const char* str, int proto) 353{ 354 uint8_t i; 355 356 if (proto == IPPROTO_TCP) { 357 i = 0; 358 while (i < TCP_NSTATES) { 359 if (strcmp(str, tcpstates[i]) == 0) 360 return i; 361 i++; 362 } 363 yyfatal("Invalid peer state"); 364 365 } else { 366 if (proto == IPPROTO_UDP) { 367 const char* mystates[] = PFUDPS_NAMES; 368 i = 0; 369 370 while (i < PFUDPS_NSTATES) { 371 if (strcmp(str, mystates[i]) == 0) 372 return i; 373 i++; 374 } 375 376 yyfatal("Invalid peer state"); 377 } else { 378 const char *mystates[] = PFOTHERS_NAMES; 379 i = 0; 380 381 while (i < PFOTHERS_NSTATES) { 382 if (strcmp(str, mystates[i]) == 0) 383 return i; 384 i++; 385 } 386 387 yyfatal("Invalid peer state"); 388 } 389 } 390 /*NOTREACHED*/ 391 return 0; 392} 393 394static bool 395strtou32(const char* str, uint32_t* res) 396{ 397 uintmax_t u; 398 errno = 0; 399 u = strtoumax(str, NULL, 10); 400 if (errno == ERANGE && u == UINTMAX_MAX) 401 return false; 402 if (u > UINT32_MAX) 403 return false; 404 *res = (uint32_t) u; 405 return true; 406} 407 408static bool 409retrieve_seq(const char* str, struct pfsync_state_peer* peer) 410{ 411 const char* p, *p_colon, *p_comma; 412 char buf[100]; 413 size_t size; 414 415 if (str == NULL || *str == '\0') 416 return false; 417 418 if (*str != '[' || *(str+(strlen(str) -1)) != ']') 419 return false; 420 421 p = str; 422 p_colon = NULL; 423 p_comma = NULL; 424 while (*p != '\0') { 425 if (*p == ':') { 426 if (p_colon !=NULL) 427 return false; 428 else 429 p_colon = p; 430 } 431 432 if (*p == ',') { 433 if (p_comma != NULL) 434 return false; 435 else 436 p_comma = p; 437 } 438 p++; 439 } 440 441 size = p_colon - str; 442 if (size > sizeof(buf)) 443 return false; 444 memcpy(buf, str+1, size-1); 445 buf[size-1] = '\0'; 446 447 if (!strtou32(buf, &peer->seqlo)) 448 return false; 449 450 451 if (p_comma == NULL) 452 size = str + strlen(str) - 1 - p_colon; 453 else 454 size = p_comma - p_colon; 455 456 if (size > sizeof(buf)) 457 return false; 458 memcpy(buf, p_colon+1, size -1); 459 buf[size-1] = '\0'; 460 461 if (!strtou32(buf, &peer->seqhi)) 462 return false; 463 464 if (p_comma == NULL) { 465 peer->seqdiff = 0; 466 } else { 467 size = str + strlen(str) - 1 - p_comma; 468 if (size > sizeof(buf)) 469 return false; 470 memcpy(buf, p_comma +1, size -1); 471 buf[size-1] = '\0'; 472 473 if (!strtou32(buf, &peer->seqdiff)) 474 return false; 475 } 476 477 return true; 478} 479 480static void 481add_state(void) 482{ 483 int idx; 484 485 if (allocated == 0) { 486 allocated = 5; 487 states->ps_buf = malloc(allocated * sizeof(struct pfsync_state)); 488 if (states->ps_buf == NULL) 489 yyfatal("Not enougth memory"); 490 } 491 492 if (allocated == (states->ps_len / sizeof(struct pfsync_state))) { 493 void *buf; 494 allocated = allocated * 2 + 1; 495 buf = realloc(states->ps_buf, allocated * sizeof(struct pfsync_state)); 496 if (buf == NULL) { 497 free(states->ps_buf); 498 yyfatal("Not enougth memory"); 499 } 500 states->ps_buf = buf; 501 } 502 503 idx = states->ps_len / sizeof(struct pfsync_state); 504} 505