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