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, &current_peer, sizeof(current_peer));
189		}
190	;
191
192dst_peer
193	: DST peer {
194			memcpy(dst_peer, &current_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, &current_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(&current_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