1 /* $NetBSD: parse.y,v 1.4 2023/03/17 17:12:54 andvar Exp $ */
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,v 1.4 2023/03/17 17:12:54 andvar Exp $");
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 int lineno;
54
55 struct pfioc_states* states;
56 size_t allocated;
57
58 // XXX it is really correct ?
59 extern const char * const tcpstates[];
60
61
62 struct pfsync_state global_state;
63 struct pfsync_state_peer *src_peer, *dst_peer;
64 struct pfsync_state_peer current_peer;
65
66 static void parse_init(void);
67 static void add_state(void);
68 static bool get_pfsync_host(const char*, struct pfsync_state_host*, sa_family_t*);
69 static uint8_t retrieve_peer_state(const char*, int);
70 static bool retrieve_seq(const char*, struct pfsync_state_peer*);
71 static bool strtou32(const char*, uint32_t*);
72
73 %}
74
75 %union {
76 uintmax_t num;
77 char* str;
78 }
79
80 %token STATE
81 %token IN OUT
82 %token ON PROTO
83 %token FROM TO USING
84 %token ID CID EXPIRE TIMEOUT
85 %token SRC DST
86 %token SEQ MAX_WIN WSCALE MSS
87 %token NOSCRUB SCRUB FLAGS TTL MODE
88 %token NUMBER STRING
89
90 %type <str> STRING
91 %type <num> NUMBER
92 %%
93
94 states
95 : /* NOTHING */
96 | state states { parse_init(); }
97 ;
98
99 state
100 : STATE direction iface proto addrs id cid expire timeout src_peer dst_peer {
101 add_state();
102 }
103 ;
104
105 direction
106 : IN {
107 global_state.direction = PF_IN;
108 src_peer = &global_state.dst;
109 dst_peer = &global_state.src;
110 }
111 | OUT {
112 global_state.direction = PF_OUT;
113 src_peer = &global_state.src;
114 dst_peer = &global_state.dst;
115 }
116 ;
117
118 iface
119 : ON STRING {
120 strlcpy(global_state.ifname, $2, sizeof(global_state.ifname));
121 free($2);
122 }
123 ;
124
125 proto
126 : PROTO STRING {
127 struct protoent *p;
128 p = getprotobyname($2);
129 if (p == NULL)
130 yyfatal("Invalid protocol name");
131 global_state.proto = p->p_proto;
132 free($2);
133 }
134 | PROTO NUMBER {
135 // check that the number may be valid proto ?
136 global_state.proto = $2;
137 }
138 ;
139
140 addrs
141 : FROM STRING TO STRING {
142 get_pfsync_host($2, &global_state.lan, &global_state.af);
143 get_pfsync_host($4, &global_state.ext, &global_state.af);
144 memcpy(&global_state.gwy, &global_state.lan, sizeof(struct pfsync_state_host));
145 free($2);
146 free($4);
147 }
148 | FROM STRING TO STRING USING STRING {
149 get_pfsync_host($2, &global_state.lan, &global_state.af);
150 get_pfsync_host($4, &global_state.ext, &global_state.af);
151 get_pfsync_host($6, &global_state.gwy, &global_state.af);
152 free($2);
153 free($4);
154 free($6);
155 }
156 ;
157
158 id
159 : ID NUMBER {
160 if ( $2 > UINT64_MAX)
161 yyfatal("id is too big");
162 uint64_t value = (uint64_t)$2;
163 memcpy(global_state.id, &value, sizeof(global_state.id));
164 }
165 ;
166
167 cid
168 : CID NUMBER {
169 if ( $2 > UINT32_MAX)
170 yyfatal("creator id is too big");
171 global_state.creatorid = (uint32_t)$2;
172 }
173 ;
174
175 expire
176 : EXPIRE NUMBER {
177 if ( $2 > UINT32_MAX)
178 yyfatal("expire time is too big");
179 global_state.expire = (uint32_t) $2;
180 }
181 ;
182
183 timeout
184 : TIMEOUT NUMBER {
185 if ($2 > UINT8_MAX)
186 yyfatal("timeout time is too big");
187 global_state.timeout = (uint8_t) $2;
188 }
189 ;
190
191 src_peer
192 : SRC peer {
193 memcpy(src_peer, ¤t_peer, sizeof(current_peer));
194 }
195 ;
196
197 dst_peer
198 : DST peer {
199 memcpy(dst_peer, ¤t_peer, sizeof(current_peer));
200 }
201 ;
202
203 peer
204 : peer_state scrub
205 | peer_state tcp_options scrub
206 ;
207
208 peer_state
209 : STATE STRING {
210 current_peer.state = retrieve_peer_state($2, global_state.proto);
211 free($2);
212 }
213 | STATE NUMBER {
214 if ( $2 > UINT8_MAX)
215 yyfatal("peer state is too big");
216 current_peer.state = $2;
217 }
218 ;
219
220 tcp_options
221 : SEQ seqs MAX_WIN NUMBER WSCALE NUMBER {
222 if ($4 > UINT16_MAX)
223 yyfatal("max_win is too big");
224 current_peer.max_win = $4;
225
226 if ($6 > UINT8_MAX)
227 yyfatal("wscale is too big");
228 current_peer.wscale = $6;
229 }
230 | SEQ seqs MAX_WIN NUMBER WSCALE NUMBER MSS NUMBER {
231 if ($4 > UINT16_MAX)
232 yyfatal("max_win is too big");
233 current_peer.max_win = $4;
234
235 if ($6 > UINT8_MAX)
236 yyfatal("wscale is too big");
237 current_peer.wscale = $6;
238
239 if ($8 > UINT16_MAX)
240 yyfatal("mss is too big");
241 current_peer.mss = $8;
242 }
243 ;
244
245 seqs
246 : STRING {
247 if (!retrieve_seq($1, ¤t_peer))
248 yyfatal("invalid seq number");
249
250 free($1);
251 }
252 ;
253
254 scrub
255 : NOSCRUB { current_peer.scrub.scrub_flag= 0;}
256 | SCRUB FLAGS NUMBER MODE NUMBER TTL NUMBER {
257 current_peer.scrub.scrub_flag= PFSYNC_SCRUB_FLAG_VALID;
258 if ($3 > UINT16_MAX)
259 yyfatal("scrub flags is too big");
260 current_peer.scrub.pfss_flags = $3;
261
262 if ($5 > UINT32_MAX)
263 yyfatal("scrub mode is too big");
264 current_peer.scrub.pfss_ts_mod = $5;
265
266 if ($7 > UINT8_MAX)
267 yyfatal("scrub ttl is too big");
268 current_peer.scrub.pfss_ttl = $7;
269 }
270 ;
271
272
273 %%
274
275 static void
276 parse_init(void)
277 {
278 memset(&global_state, 0, sizeof(global_state));
279 memset(¤t_peer, 0, sizeof(current_peer));
280 src_peer = NULL;
281 dst_peer = NULL;
282 }
283
284 static bool
get_pfsync_host(const char * str,struct pfsync_state_host * host,sa_family_t * af)285 get_pfsync_host(const char* str, struct pfsync_state_host* host, sa_family_t* af)
286 {
287 size_t count_colon, addr_len, port_len;
288 const char* p, *last_colon, *first_bracket, *last_bracket;
289 char buf[48];
290 char buf_port[6];
291
292 if (str == NULL || *str == '\0')
293 return false;
294
295 p = str;
296 last_colon = NULL;
297 count_colon = 0;
298
299 while (*p != '\0') {
300 if (*p == ':') {
301 count_colon++;
302 last_colon = p;
303 }
304 p++;
305 }
306
307 /*
308 * If no colon, it is not an expected addr
309 * If there are more than one colon, we guess that af = AF_INET6
310 */
311
312 if (count_colon == 0)
313 return false;
314
315 if (count_colon == 1)
316 *af = AF_INET;
317 else
318 *af = AF_INET6;
319
320 /*
321 * First bracket must be next character after last colon
322 * Last bracket must be the last character
323 * distance between both must be <= 7
324 */
325
326 if (*(last_colon+1) == '[')
327 first_bracket = last_colon + 1;
328 else
329 return false;
330
331 last_bracket = str + (strlen(str) - 1);
332 if (*last_bracket != ']')
333 return false;
334
335 port_len = last_bracket - first_bracket;
336 if (last_bracket - first_bracket > 7)
337 return false;
338
339 memcpy(buf_port, first_bracket +1, port_len - 1);
340 buf_port[port_len-1]= '\0';
341
342 addr_len = last_colon - str;
343 if (addr_len >= sizeof(buf))
344 return false;
345 memcpy(buf, str, addr_len);
346 buf[addr_len] = '\0';
347
348 if (inet_pton(*af, buf, &host->addr) != 1)
349 return false;
350
351 host->port = htons(atoi(buf_port));
352
353 return true;
354 }
355
356 static uint8_t
retrieve_peer_state(const char * str,int proto)357 retrieve_peer_state(const char* str, int proto)
358 {
359 uint8_t i;
360
361 if (proto == IPPROTO_TCP) {
362 i = 0;
363 while (i < TCP_NSTATES) {
364 if (strcmp(str, tcpstates[i]) == 0)
365 return i;
366 i++;
367 }
368 yyfatal("Invalid peer state");
369
370 } else {
371 if (proto == IPPROTO_UDP) {
372 const char* mystates[] = PFUDPS_NAMES;
373 i = 0;
374
375 while (i < PFUDPS_NSTATES) {
376 if (strcmp(str, mystates[i]) == 0)
377 return i;
378 i++;
379 }
380
381 yyfatal("Invalid peer state");
382 } else {
383 const char *mystates[] = PFOTHERS_NAMES;
384 i = 0;
385
386 while (i < PFOTHERS_NSTATES) {
387 if (strcmp(str, mystates[i]) == 0)
388 return i;
389 i++;
390 }
391
392 yyfatal("Invalid peer state");
393 }
394 }
395 /*NOTREACHED*/
396 return 0;
397 }
398
399 static bool
strtou32(const char * str,uint32_t * res)400 strtou32(const char* str, uint32_t* res)
401 {
402 uintmax_t u;
403 errno = 0;
404 u = strtoumax(str, NULL, 10);
405 if (errno == ERANGE && u == UINTMAX_MAX)
406 return false;
407 if (u > UINT32_MAX)
408 return false;
409 *res = (uint32_t) u;
410 return true;
411 }
412
413 static bool
retrieve_seq(const char * str,struct pfsync_state_peer * peer)414 retrieve_seq(const char* str, struct pfsync_state_peer* peer)
415 {
416 const char* p, *p_colon, *p_comma;
417 char buf[100];
418 size_t size;
419
420 if (str == NULL || *str == '\0')
421 return false;
422
423 if (*str != '[' || *(str+(strlen(str) -1)) != ']')
424 return false;
425
426 p = str;
427 p_colon = NULL;
428 p_comma = NULL;
429 while (*p != '\0') {
430 if (*p == ':') {
431 if (p_colon !=NULL)
432 return false;
433 else
434 p_colon = p;
435 }
436
437 if (*p == ',') {
438 if (p_comma != NULL)
439 return false;
440 else
441 p_comma = p;
442 }
443 p++;
444 }
445
446 size = p_colon - str;
447 if (size > sizeof(buf))
448 return false;
449 memcpy(buf, str+1, size-1);
450 buf[size-1] = '\0';
451
452 if (!strtou32(buf, &peer->seqlo))
453 return false;
454
455
456 if (p_comma == NULL)
457 size = str + strlen(str) - 1 - p_colon;
458 else
459 size = p_comma - p_colon;
460
461 if (size > sizeof(buf))
462 return false;
463 memcpy(buf, p_colon+1, size -1);
464 buf[size-1] = '\0';
465
466 if (!strtou32(buf, &peer->seqhi))
467 return false;
468
469 if (p_comma == NULL) {
470 peer->seqdiff = 0;
471 } else {
472 size = str + strlen(str) - 1 - p_comma;
473 if (size > sizeof(buf))
474 return false;
475 memcpy(buf, p_comma +1, size -1);
476 buf[size-1] = '\0';
477
478 if (!strtou32(buf, &peer->seqdiff))
479 return false;
480 }
481
482 return true;
483 }
484
485 static void
add_state(void)486 add_state(void)
487 {
488
489 if (allocated == 0) {
490 allocated = 5;
491 states->ps_buf = malloc(allocated * sizeof(struct pfsync_state));
492 if (states->ps_buf == NULL)
493 yyfatal("Not enough memory");
494 }
495
496 if (allocated == (states->ps_len / sizeof(struct pfsync_state))) {
497 void *buf;
498 allocated = allocated * 2 + 1;
499 buf = realloc(states->ps_buf, allocated * sizeof(struct pfsync_state));
500 if (buf == NULL) {
501 free(states->ps_buf);
502 yyfatal("Not enough memory");
503 }
504 states->ps_buf = buf;
505 }
506
507 }
508