xref: /netbsd/usr.sbin/pf/pfs/parse.y (revision 6550d01e)
1 /* $NetBSD: parse.y,v 1.1 2010/05/07 17:41:58 degroote 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.1 2010/05/07 17:41:58 degroote 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 // XXX it is really correct ?
54 extern const char * const tcpstates[];
55 
56 
57 struct pfsync_state global_state;
58 struct pfsync_state_peer *src_peer, *dst_peer;
59 struct pfsync_state_peer current_peer;
60 
61 static void parse_init(void);
62 static void add_state(void);
63 static bool get_pfsync_host(const char*, struct pfsync_state_host*, sa_family_t*);
64 static uint8_t retrieve_peer_state(const char*, int);
65 static bool retrieve_seq(const char*, struct pfsync_state_peer*);
66 static 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 
89 states
90 	: /* NOTHING */
91 	| state states  { parse_init(); }
92 	;
93 
94 state
95 	: STATE direction iface proto addrs id cid expire timeout src_peer dst_peer {
96 			add_state();
97 		}
98 	;
99 
100 direction
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 
113 iface
114 	: ON STRING {
115 			strlcpy(global_state.ifname, $2, sizeof(global_state.ifname));
116 			free($2);
117 		}
118 	;
119 
120 proto
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 
135 addrs
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 
153 id
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 
162 cid
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 
170 expire
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 
178 timeout
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 
186 src_peer
187 	: SRC peer {
188 			memcpy(src_peer, &current_peer, sizeof(current_peer));
189 		}
190 	;
191 
192 dst_peer
193 	: DST peer {
194 			memcpy(dst_peer, &current_peer, sizeof(current_peer));
195 		}
196 	;
197 
198 peer
199 	: peer_state scrub
200 	| peer_state tcp_options scrub
201 	;
202 
203 peer_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 
215 tcp_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 
240 seqs
241 	: STRING {
242 		if (!retrieve_seq($1, &current_peer))
243 			yyfatal("invalid seq number");
244 
245 		free($1);
246 		}
247 	;
248 
249 scrub
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 
270 static void
271 parse_init(void)
272 {
273 	memset(&global_state, 0, sizeof(global_state));
274 	memset(&current_peer, 0, sizeof(current_peer));
275 	src_peer = NULL;
276 	dst_peer = NULL;
277 }
278 
279 static bool
280 get_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 
351 static uint8_t
352 retrieve_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 
394 static bool
395 strtou32(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 
408 static bool
409 retrieve_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 
480 static void
481 add_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