1/*	$Id: radvd_token.l,v 1.1.1.1 2006-12-04 00:45:31 pmoutarl Exp $	*/
2
3/*
4 * Copyright (C) International Business Machines  Corp., 2003
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 * 3. Neither the name of the project nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 */
30
31/* Author: Shirley Ma, xma@us.ibm.com */
32
33%option noyywrap
34
35%{
36#include <stdio.h>
37#include <string.h>
38#include <sys/types.h>
39#include <sys/file.h>
40#include <sys/stat.h>
41#include <unistd.h>
42#include <signal.h>
43
44#include <netinet/in.h>
45#include <arpa/inet.h>
46
47#include <errno.h>
48#include <syslog.h>
49#include <string.h>
50
51#include "queue.h"
52#include "dhcp6.h"
53#include "config.h"
54#include "common.h"
55#include "lease.h"
56
57#ifndef PATH_RADVD_PID
58#define	PATH_RADVD_PID "/var/run/radvd/radvd.pid"
59#endif
60
61#define YYABORT(msg) dprintf(LOG_ERR, msg " %s lineno %d.", \
62	yytext, num_lines)
63
64#define ABORT   do {    \
65	YYABORT("radvd.conf parse error");   \
66	remove(RADVD_CONF_DHCPV6_FILE); \
67	exit(1);        \
68} while (0)
69
70#undef yywrap
71
72#define DHCP6_MARK "# update prefix from DHCPv6 server\n"
73
74extern struct dhcp6_if *dhcp6_if;
75int radvd_parse __P((struct dhcp6_iaidaddr *, int));
76
77static struct dhcp6_list ori_prefix_list;
78static struct dhcp6_iaidaddr *client6iaid;
79static struct dhcp6_list previous_prefix_list;
80static FILE *dhcp6_radvd_file;
81static int update_flag;
82static int oldfd, newfd;
83static int num_lines = 0;
84static int brace = 0;
85static int update = 0;
86
87static int yywrap __P((void));
88static int update_radvd __P((void));
89
90%}
91
92whitespace      ([ \t])+
93digit		[0-9]
94string		[a-zA-Z0-9:\.\/][a-zA-Z0-9:\.\/]*
95nl              \n
96lbrace		"{"
97rbrace		"}"
98mark		DHCP6_MARK
99comment		\#.*
100
101%s S_INTERFACE S_IFNAME S_ORIPREFIX S_NEWPREFIX S_DHCP6
102
103%%
104
105{nl}            {fprintf(dhcp6_radvd_file, yytext);
106		num_lines++;}
107
108{whitespace}    {fprintf(dhcp6_radvd_file, yytext);}
109
110
111"interface"	{fprintf(dhcp6_radvd_file, yytext);
112		BEGIN S_INTERFACE;}
113
114<S_INTERFACE>{string} {
115		char *requested_if;
116		fprintf(dhcp6_radvd_file, yytext);
117		if ( ( ( (requested_if = get_if_option(&(dhcp6_if->option_list),
118							DECL_PREFIX_DELEGATION_INTERFACE
119						       )
120			   ) != 0L
121			 )
122		       &&( strcmp(yytext, requested_if) == 0)
123		     )
124                   ||((requested_if == 0L) && (strcmp(yytext, dhcp6_if->ifname)==0))
125		   )
126		{
127			TAILQ_INIT(&ori_prefix_list);
128			TAILQ_INIT(&previous_prefix_list);
129			BEGIN S_IFNAME;
130		}else
131		        BEGIN INITIAL;
132}
133
134<S_IFNAME>{comment} {
135		if (strstr(yytext, DHCP6_MARK) != NULL) {
136			BEGIN S_DHCP6;
137		} else
138			fprintf(dhcp6_radvd_file, yytext);}
139
140<S_IFNAME>"prefix" {fprintf(dhcp6_radvd_file, yytext);
141		BEGIN S_ORIPREFIX;}
142
143<S_IFNAME>{lbrace} {fprintf(dhcp6_radvd_file, yytext);
144		brace++;
145		BEGIN S_IFNAME;}
146
147<S_IFNAME>{rbrace} {struct dhcp6_lease *lv;
148		brace--;
149		if (brace == 0 && update_flag == ADDR_UPDATE) {
150			/* add prefix which is not on the original prefix list
151			 * check the lifetime value, AdvValidLifetime,
152			 * AdvPreferredLifetime: infinity or XXX seconds */
153			for (lv = TAILQ_FIRST(&client6iaid->lease_list); lv;
154			     lv = TAILQ_NEXT(lv, link)) {
155				if (!addr_on_addrlist(&ori_prefix_list, &lv->lease_addr) &&
156				    !addr_on_addrlist(&previous_prefix_list,
157				    &lv->lease_addr)){
158					fprintf(dhcp6_radvd_file, DHCP6_MARK);
159					fprintf(dhcp6_radvd_file, "\tprefix %s/%d {};\n",
160						in6addr2str(&(lv->lease_addr.addr), 0),
161						lv->lease_addr.plen);
162					update = 1;
163				}
164				dprintf(LOG_INFO, "update radvd.conf with DHCPv6 prefixes");
165			}
166			fprintf(dhcp6_radvd_file, yytext);
167			BEGIN INITIAL;
168		} else {
169			fprintf(dhcp6_radvd_file, yytext);
170			BEGIN S_IFNAME;}
171		}
172
173<S_DHCP6>"prefix" {BEGIN S_NEWPREFIX;}
174<S_NEWPREFIX>{string} {
175		/* update the prefix from DHCPv6 server
176		 * if the new list doesn't include this prefix remove it */
177		struct dhcp6_listval *lv;
178		/* create orignal prefix list */
179		if ((lv = (struct dhcp6_listval *)malloc(sizeof(*lv))) == NULL) {
180			ABORT;
181		}
182		memset(lv, 0, sizeof(*lv));
183		if (strstr(yytext, "/") == NULL)
184			ABORT;
185		if (inet_pton(AF_INET6, strtok(yytext, "/"), &lv->val_dhcp6addr.addr) < 1) {
186			ABORT;
187		}
188		lv->val_dhcp6addr.type = IAPD;
189		lv->val_dhcp6addr.plen = atoi(strtok(NULL, "/"));
190		lv->val_dhcp6addr.status_code = DH6OPT_STCODE_UNDEFINE;
191		/* remove the prefix list for release */
192		if (update_flag == ADDR_REMOVE &&
193		    !addr_on_addrlist((struct dhcp6_list *)&client6iaid->lease_list,
194		    &lv->val_dhcp6addr)) {
195			dprintf(LOG_DEBUG, "remove DHCPv6 assgined prefix in radvd.conf");
196			update = 1;
197		} else {
198			TAILQ_INSERT_TAIL(&previous_prefix_list, lv, link);
199			fprintf(dhcp6_radvd_file, DHCP6_MARK);
200			fprintf(dhcp6_radvd_file, "\tprefix %s/%d {};\n",
201				in6addr2str(&(lv->val_dhcp6addr.addr), 0),
202				lv->val_dhcp6addr.plen);
203		}
204		BEGIN S_NEWPREFIX;}
205<S_NEWPREFIX>{rbrace}";" {BEGIN S_IFNAME;}
206<S_NEWPREFIX>.	{;}
207
208<S_ORIPREFIX>{string} {
209		struct dhcp6_listval *lv;
210		fprintf(dhcp6_radvd_file, yytext);
211		/* create orignal prefix list */
212		if ((lv = (struct dhcp6_listval *)malloc(sizeof(*lv))) == NULL) {
213			ABORT;
214		}
215		memset(lv, 0, sizeof(*lv));
216		if (inet_pton(AF_INET6, strtok(yytext, "/"), &lv->val_dhcp6addr.addr) < 1) {
217			ABORT;
218		}
219		lv->val_dhcp6addr.type = IAPD;
220		lv->val_dhcp6addr.plen = atoi(strtok(NULL, "/"));
221		lv->val_dhcp6addr.status_code = DH6OPT_STCODE_UNDEFINE;
222		TAILQ_INSERT_TAIL(&ori_prefix_list, lv, link);
223		BEGIN S_IFNAME;
224		}
225
226{comment}	{fprintf(dhcp6_radvd_file, yytext);}
227.		{fprintf(dhcp6_radvd_file, yytext);}
228
229%%
230
231/* parse radvd.conf
232 * create a new radvd.conf.dhcpv6
233 * mv /etc/radvd.conf to /etc/radvd.conf.V6BAK
234 * link radvd.conf to radvd.conf.dhcpv6
235 */
236int
237radvd_parse(struct dhcp6_iaidaddr *client6_iaid, int flag)
238{
239	struct stat buf;
240	char pidstr[128];
241	sprintf(pidstr, "%d", getpid());
242	strcpy(radvd_dhcpv6_file, RADVD_CONF_DHCPV6_FILE);
243	strcat(radvd_dhcpv6_file, pidstr);
244	while ((oldfd = open(RADVD_CONF_FILE, O_EXCL)) < 0) {
245		switch (errno) {
246		case ENOENT:
247			/* exclusive open of new radvd.conf.dhcpv6 file */
248			if ((newfd = open(radvd_dhcpv6_file,
249			    O_CREAT|O_EXCL|O_WRONLY|O_TRUNC, 0644))
250			    < 0) {
251				dprintf(LOG_ERR, "%s"
252					" failed to open radvd.conf.dhcpv6 file", FNAME);
253				return (-1);
254			}
255			if ((dhcp6_radvd_file = fdopen(newfd, "w")) == NULL) {
256				dprintf(LOG_ERR, "%s"
257					" failed to fdopen radvd.conf.dhcpv6 file", FNAME);
258				return (-1);
259			}
260			return(update_radvd());
261		case EACCES:
262			sleep(1);
263			continue;
264		default:
265			dprintf(LOG_ERR, "radvd_parse: fopen(%s): %s",
266				RADVD_CONF_FILE, strerror(errno));
267			return (-1);
268		}
269	}
270	if (lstat(RADVD_CONF_FILE, &buf)) {
271		dprintf(LOG_ERR, "lstat radvd.conf file failed");
272		return (-1);
273	}
274	if ((yyin = fdopen(oldfd, "r")) == NULL) {
275		dprintf(LOG_ERR, "radvd_parse: fdopen(%s): %s",
276			RADVD_CONF_FILE, strerror(errno));
277		return (-1);
278	}
279	/* exclusive open of new radvd.conf.dhcpv6$pid file */
280	if ((newfd = open(radvd_dhcpv6_file,
281	    O_CREAT|O_EXCL|O_WRONLY|O_TRUNC, buf.st_mode)) < 0) {
282		dprintf(LOG_ERR, "%s"
283			" failed to open radvd.conf.dhcpv6 file", FNAME);
284		return (-1);
285	}
286	if ((dhcp6_radvd_file = fdopen(newfd, "w")) == NULL) {
287		dprintf(LOG_ERR, "%s"
288			" failed to fdopen radvd.conf.dhcpv6$pid file", FNAME);
289		return (-1);
290	}
291	client6iaid = client6_iaid;
292	update_flag = flag;
293	return yylex();
294}
295
296static int
297update_radvd(void)
298{
299	struct stat buf;
300	if (fflush(dhcp6_radvd_file) == EOF) {
301		dprintf(LOG_ERR, "%s" "write radvd.conf.dhcpv6 file fflush failed %s",
302			FNAME, strerror(errno));
303		return (-1);
304	}
305	if (fsync(newfd) < 0) {
306		dprintf(LOG_ERR, "%s" "write radvd.conf.dhcpv6 file failed %s",
307			FNAME, strerror(errno));
308		return (-1);
309	}
310	fclose(dhcp6_radvd_file);
311	close(newfd);
312	if (!lstat(RADVD_CONF_FILE, &buf)) {
313		if (lstat(RADVD_CONF_BAK_FILE, &buf) < 0 && errno == ENOENT) {
314			/* save the orignal file as a bak file */
315			if (link(RADVD_CONF_FILE, RADVD_CONF_BAK_FILE)) {
316				dprintf(LOG_ERR, "%s"
317					" backup failed for radvd.conf file", FNAME);
318				return (-1);
319			}
320		}
321	}
322	/* rename the new conf file as the radvd.conf file */
323	if (rename(radvd_dhcpv6_file, RADVD_CONF_FILE)) {
324		dprintf(LOG_ERR, "%s" " rename failed for radvd.conf file", FNAME);
325		return(-1);
326	}
327	return (0);
328}
329
330static int
331yywrap(void)
332{
333	int fd;
334	char buff[8];
335	pid_t radvd_pid;
336	(void)update_radvd();
337	if (update) {
338		if ((fd = open(PATH_RADVD_PID, O_RDONLY)) > 0) {
339			if (read(fd, buff, sizeof(buff)) > 0) {
340				radvd_pid = strtol(buff, NULL, 10);
341				if (radvd_pid == 0)
342					dprintf(LOG_INFO, "invalid radvd pid");
343				else if (kill(radvd_pid, SIGHUP) != 0)
344					dprintf(LOG_INFO, "failed to reload radvd");
345			} else
346				dprintf(LOG_INFO, "failed to read radvd pid file");
347			close(fd);
348		} else
349			dprintf(LOG_INFO, "failed to open radvd pid file");
350	}
351	fclose(yyin);
352	close(oldfd);
353	return 1;
354}
355