1/*	$Id: lease_token.l,v 1.1.1.1 2006/12/04 00:45:31 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 * SUCH DAMAGE.
30 */
31
32/* Author: Shirley Ma, xma@us.ibm.com */
33
34%option noyywrap
35
36%{
37
38#include <stdio.h>
39#include <string.h>
40#include <sys/types.h>
41#include <sys/time.h>
42#include <sys/timeb.h>
43#include <syslog.h>
44#include <errno.h>
45#include <sys/socket.h>
46#include <arpa/inet.h>
47
48#include <net/if.h>
49#include <linux/sockios.h>
50#include "queue.h"
51#include "dhcp6.h"
52#include "config.h"
53#include "common.h"
54#include "lease.h"
55#include "hash.h"
56#include "timer.h"
57
58#define LEASE_ADDR_FLAG	0x01
59#define LEASE_DUID_FLAG 0x02
60#define LEASE_IAID_FLAG 0x04
61#define LEASE_SDATE_FLAG 0x08
62
63#define LEASE_VTIME_FLAG 0x10
64#define LEASE_PTIME_FLAG 0x20
65#define LEASE_RNTIME_FLAG 0x40
66#define LEASE_RBTIME_FLAG 0x80
67
68#define LEASE_HNAME_FLAG 0x100
69#define LEASE_LL_FLAG 0x200
70
71#define VALID_LEASE_FLAG(a) 					\
72	((a & LEASE_ADDR_FLAG) && (a & LEASE_SDATE_FLAG) && 	\
73	 (a & LEASE_VTIME_FLAG) && (a & LEASE_PTIME_FLAG) &&	\
74	 (a & LEASE_IAID_FLAG) && (a & LEASE_RNTIME_FLAG) &&	\
75	 (a & LEASE_RBTIME_FLAG) && (a & LEASE_DUID_FLAG))
76
77#define YYABORT(msg) dprintf(LOG_ERR, msg " %s lineno %d.", \
78		yytext, num_lines)
79
80#define	ABORT	do { 	\
81	YYABORT("lease parse error");	\
82	exit(1);	\
83} while (0)
84
85extern struct dhcp6_timer *dhcp6_lease_timo __P((void *));
86extern struct dhcp6_iaidaddr client6_iaidaddr;
87
88static int num_lines = 1;
89static struct dhcp6_lease *lease_rec;
90static struct client6_if client6_info;
91
92static u_int16_t lease_flags = 0;
93
94static int add_lease __P((struct dhcp6_iaidaddr *, struct dhcp6_lease *));
95static int remove_lease __P((struct dhcp6_lease *));
96
97%}
98
99digit           [0-9]
100number          ({digit})+
101decimal         ({number}"."{number})
102hexdigit        ([a-f]|[A-F]|[0-9])
103hexpair		({hexdigit}{hexdigit})
104ipv4addr	({digit}{1,3}"."{digit}{1,3}"."{digit}{1,3}"."{digit}{1,3})
105addr_head	("::"|{hexdigit}{1,4}(":"|"::"))
106addr_tail	({hexdigit}{1,4}|({hexdigit}{1,4}"::")|{ipv4addr})?
107addr_body	({hexdigit}{1,4}(":"|"::"))*
108ipv6addr	{addr_head}{addr_body}{addr_tail}
109duid_id 	{hexpair}(:{hexpair})*
110whitespace      ([ \t])+
111string          [a-zA-Z]([a-zA-Z]|{digit})*(":"{digit}+)?
112lbrace		\{
113rbrace		\}
114slash		\/
115colon		\:
116semi		\;
117nl		\n
118comment		\#.*
119
120%s S_LEASE
121%s S_PLEN
122%s S_HNAME
123%s S_LL
124%s S_DUID
125%s S_SDUID
126%s S_IAID
127%s S_IATYPE
128%s S_RNTIME
129%s S_RBTIME
130%s S_PTIME
131%s S_VTIME
132%s S_DATE
133%s S_STATE
134%s S_IGNORE
135
136%%
137
138{comment}	{;/* ignore comments */}
139{nl}		{num_lines++; BEGIN INITIAL;}
140{whitespace}	{;}
141{semi}		{;}
142	/* lease parser */
143"lease" {
144	lease_rec = (struct dhcp6_lease *)malloc(sizeof(*lease_rec));
145	if (lease_rec == NULL) {
146		YYABORT("failed to allocate memory for a lease");
147		ABORT;
148	}
149	memset(lease_rec, 0, sizeof(*lease_rec));
150	memset(&client6_info, 0, sizeof(client6_info));
151	lease_flags = 0;
152	BEGIN S_LEASE;}
153"hostname:" {BEGIN S_HNAME;}
154"linklocal:" {BEGIN S_LL;}
155"DUID:" {BEGIN S_DUID;}
156"SDUID:" {BEGIN S_SDUID;}
157"IAID:" {BEGIN S_IAID;}
158"RenewTime:" {BEGIN S_RNTIME;}
159"RebindTime:" {BEGIN S_RBTIME;}
160"ValidLifeTime:"	{BEGIN S_VTIME;}
161"PreferredLifeTime:" {BEGIN S_PTIME;}
162"start date:" {BEGIN S_DATE;}
163"(start_date" {BEGIN S_IGNORE;}
164"state:" {BEGIN S_STATE;}
165{rbrace} {if (do_iaidaddr_hash(lease_rec, &client6_info) != 0) {
166		ABORT;
167	  }
168	  else {
169		BEGIN INITIAL;
170	 }
171}
172<S_LEASE>{ipv6addr} {
173	struct in6_addr addr;
174	if(inet_pton(AF_INET6, yytext, &addr) < 1) {
175		YYABORT("invalid address");
176		free(lease_rec);
177		ABORT;
178	}
179	memcpy(&lease_rec->lease_addr.addr, &addr,
180			sizeof(lease_rec->lease_addr.addr));
181	lease_flags |= LEASE_ADDR_FLAG;}
182<S_LEASE>{slash} {BEGIN S_PLEN;}
183<S_LEASE>. {ABORT;}
184<S_PLEN>{number} {lease_rec->lease_addr.plen = (u_int8_t)atoi(yytext);}
185<S_PLEN>{lbrace} {;}
186<S_PLEN>. {ABORT;}
187<S_HNAME>{string} {strncpy(lease_rec->hostname, yytext, sizeof(yytext));
188		lease_flags |= LEASE_HNAME_FLAG;}
189<S_HNAME>. {ABORT;}
190<S_LL>{ipv6addr} {
191	struct in6_addr addr;
192	if(inet_pton(AF_INET6, yytext, &addr) < 1) {
193		YYABORT("invalid address");
194		free(lease_rec);
195		ABORT;
196	}
197	memcpy(&lease_rec->linklocal, &addr,
198			sizeof(lease_rec->linklocal));
199	lease_flags |= LEASE_LL_FLAG;}
200<S_LL>. {ABORT;}
201<S_DUID>{duid_id} {configure_duid(yytext, &client6_info.clientid);
202		lease_flags |= LEASE_DUID_FLAG;}
203<S_DUID>. {ABORT;}
204<S_SDUID>{duid_id} {configure_duid(yytext, &client6_info.serverid);}
205<S_SDUID>. {ABORT;}
206<S_IAID>{number} {client6_info.iaidinfo.iaid = strtoll(yytext, NULL, 10);
207		lease_flags |= LEASE_IAID_FLAG;}
208<S_IAID>"type:" {BEGIN S_IATYPE;}
209<S_IAID>. {ABORT;}
210<S_IATYPE>{number} {lease_rec->lease_addr.type = (u_int8_t)atoi(yytext);
211		client6_info.type = (u_int8_t)atoi(yytext);}
212<S_IATYPE>. {ABORT;}
213<S_RNTIME>{number} {client6_info.iaidinfo.renewtime = (u_int32_t)strtoul(yytext, NULL, 10);
214		lease_flags |= LEASE_RNTIME_FLAG;}
215<S_RNTIME>. {ABORT;}
216<S_RBTIME>{number} {client6_info.iaidinfo.rebindtime = (u_int32_t)strtoul(yytext, NULL, 10);
217		lease_flags |= LEASE_RBTIME_FLAG;}
218<S_RBTIME>. {ABORT;}
219<S_VTIME>{number} {lease_rec->lease_addr.validlifetime = (u_int32_t)strtoul(yytext, NULL, 10);
220		lease_flags |= LEASE_VTIME_FLAG;}
221<S_VTIME>. {ABORT;}
222<S_PTIME>{number} {lease_rec->lease_addr.preferlifetime = (u_int32_t)strtoul(yytext, NULL, 10);
223		lease_flags |= LEASE_PTIME_FLAG;}
224<S_PTIME>. {ABORT;}
225<S_DATE>{number} {lease_rec->start_date = (time_t)strtoul(yytext, NULL, 10);
226		lease_flags |= LEASE_SDATE_FLAG;}
227<S_DATE>. {ABORT;}
228<S_STATE>{number} {lease_rec->state = (state_t)atoi(yytext);}
229<S_STATE>. {ABORT;}
230<S_IGNORE>.	{;}
231
232. {ABORT;}
233
234%%
235
236
237void
238lease_parse(file)
239        FILE *file;
240{
241
242	fseek(file, 0, 0);
243	yyin = file;
244	yylex();
245	return;
246}
247
248int
249do_iaidaddr_hash(lease_rec, key)
250	struct dhcp6_lease *lease_rec;
251	struct client6_if *key;
252{
253	struct dhcp6_iaidaddr *iaidaddr;
254	struct dhcp6_lease *found_lease;
255	struct timeb now;
256	time_t offset;
257	struct timeval timo;
258	double d;
259
260	if (!VALID_LEASE_FLAG(lease_flags) ||
261	    (lease_rec->lease_addr.preferlifetime > lease_rec->lease_addr.validlifetime)) {
262			dprintf(LOG_ERR, "parse an invalid lease %s/%d within line %d",
263				in6addr2str(&lease_rec->lease_addr.addr, 0),
264				lease_rec->lease_addr.plen, num_lines);
265			return (-1);
266	}
267	ftime(&now);
268	offset = now.time - lease_rec->start_date;
269	dprintf(LOG_DEBUG, "now : %ld; offset: %ld ", now.time, offset);
270	if (offset > lease_rec->lease_addr.validlifetime) {
271		dprintf(LOG_INFO, "This lease addr %s/%d has been expired.",
272			in6addr2str(&lease_rec->lease_addr.addr, 0),
273			lease_rec->lease_addr.plen);
274		free(lease_rec);
275		return (0);
276	}
277
278	if (lease_rec->state == INVALID) {
279		dprintf(LOG_INFO, "This lease addr %s/%d is invalid. Removing.",
280		        in6addr2str(&lease_rec->lease_addr.addr, 0),
281		        lease_rec->lease_addr.plen);
282		found_lease = hash_search(lease_hash_table, &lease_rec->lease_addr);
283		/* remove the previous old lease */
284		if(found_lease) {
285			remove_lease(found_lease);
286		}
287		free(lease_rec);
288		return (0);
289	}
290	if (dhcp6_mode == DHCP6_MODE_CLIENT) {
291		if (add_lease(&client6_iaidaddr, lease_rec) != 0)
292			return (-1);
293		else {
294			memcpy(&client6_iaidaddr.client6_info, &client6_info,
295				sizeof(client6_iaidaddr.client6_info));
296			dprintf(LOG_DEBUG, "hash add client iaidaddr type %d for "
297				" duid %s for iaid %u",
298				client6_iaidaddr.client6_info.type,
299				duidstr(&client6_iaidaddr.client6_info.clientid),
300				client6_iaidaddr.client6_info.iaidinfo.iaid);
301			return (0);
302		}
303	}
304	iaidaddr = (struct dhcp6_iaidaddr *)hash_search(server6_hash_table, key);
305	if (iaidaddr){
306		if (iaidaddr->client6_info.iaidinfo.iaid != client6_info.iaidinfo.iaid ||
307		    duidcmp(&iaidaddr->client6_info.clientid, &client6_info.clientid)) {
308			dprintf(LOG_DEBUG, "%s"
309				"this client iaidinfo is different to others", FNAME);
310			return (-1);
311		}
312		found_lease = hash_search(lease_hash_table, &lease_rec->lease_addr);
313		/* remove the previous old lease */
314		if(found_lease) {
315			if (found_lease->start_date < lease_rec->start_date) {
316				dprintf(LOG_DEBUG, "%s" "remove old lease %s/%d", FNAME,
317					in6addr2str(&lease_rec->lease_addr.addr, 0),
318					lease_rec->lease_addr.plen);
319				/* update the client6_info too */
320				memcpy(&iaidaddr->client6_info, &client6_info,
321					sizeof(iaidaddr->client6_info));
322				iaidaddr->start_date = lease_rec->start_date;
323				remove_lease(found_lease);
324			} else {
325				free(lease_rec);
326				return (0);
327			}
328		}
329	} else {
330		iaidaddr = (struct dhcp6_iaidaddr *)malloc(sizeof(*iaidaddr));
331	  	if (!iaidaddr) {
332			dprintf(LOG_ERR, "%s" "failed to allocate memory", FNAME);
333			return (-1);
334	  	}
335		memset(iaidaddr, 0, sizeof(*iaidaddr));
336		TAILQ_INIT(&iaidaddr->lease_list);
337		/* get the client info */
338		memcpy(&iaidaddr->client6_info, &client6_info, sizeof(iaidaddr->client6_info));
339		if (hash_add(server6_hash_table,
340		    &iaidaddr->client6_info, iaidaddr) != 0) {
341			dprintf(LOG_ERR, "%s" "hash add failed", FNAME);
342			free(iaidaddr);
343			return (-1);
344		}
345		dprintf(LOG_DEBUG, "hash add client iaidaddr %u type %d for duid %s",
346			iaidaddr->client6_info.iaidinfo.iaid,
347			iaidaddr->client6_info.type,
348			duidstr(&iaidaddr->client6_info.clientid));
349	}
350	if (add_lease(iaidaddr, lease_rec) != 0)
351		return (-1);
352	iaidaddr->state = ACTIVE;
353	iaidaddr->start_date = lease_rec->start_date;
354	d = get_max_validlifetime(iaidaddr) - offset;
355	timo.tv_sec = (long)d;
356	timo.tv_usec = 0;
357	/* update the start date and timer */
358	if (!iaidaddr->timer && (iaidaddr->timer =
359	     dhcp6_add_timer(dhcp6_iaidaddr_timo, iaidaddr)) == NULL) {
360			dprintf(LOG_ERR, "%s" "failed to add a timer for iaid %u",
361			FNAME, iaidaddr->client6_info.iaidinfo.iaid);
362	 	return (-1);
363	}
364	dhcp6_set_timer(&timo, iaidaddr->timer);
365	return (0);
366}
367
368static int
369add_lease(iaidaddr, lease)
370	struct dhcp6_iaidaddr *iaidaddr;
371	struct dhcp6_lease *lease;
372{
373	struct timeval timo;
374	struct timeb now;
375	time_t offset;
376	struct dhcp6_lease *found_lease;
377	double d;
378	/* set up timer out for lease, iaidaddr timer out will be set up in
379	 * post_conf_lease */
380	ftime(&now);
381	offset = now.time - lease_rec->start_date;
382	if (dhcp6_mode == DHCP6_MODE_CLIENT) {
383		if ((found_lease = dhcp6_find_lease(iaidaddr, &lease_rec->lease_addr))
384					!= NULL) {
385			if (!(found_lease->start_date > lease_rec->start_date)) {
386				dprintf(LOG_DEBUG, "%s" "remove old lease %s/%d", FNAME,
387					in6addr2str(&lease_rec->lease_addr.addr, 0),
388					lease_rec->lease_addr.plen);
389				if (iaidaddr->client6_info.iaidinfo.iaid !=
390						client6_info.iaidinfo.iaid) {
391					dprintf(LOG_DEBUG, "%s"
392					" this lease %s client iaidinfo is different"
393					" to the previous lease",
394					FNAME,  in6addr2str(&lease_rec->lease_addr.addr, 0));
395					return (-1);
396				}
397				iaidaddr->client6_info.iaidinfo.renewtime =
398						client6_info.iaidinfo.renewtime;
399				iaidaddr->client6_info.iaidinfo.rebindtime =
400						client6_info.iaidinfo.rebindtime;
401				iaidaddr->start_date = lease_rec->start_date;
402				remove_lease(found_lease);
403			} else {
404				free(lease_rec);
405				return (0);
406			}
407		}
408		/* if this is the first lease */
409		if (TAILQ_EMPTY(&iaidaddr->lease_list)) {
410			if (iaidaddr->client6_info.iaidinfo.iaid !=
411					client6_info.iaidinfo.iaid) {
412				dprintf(LOG_DEBUG, "%s"
413					" this lease %s client iaidinfo is different"
414					" to the previous lease",
415					FNAME,  in6addr2str(&lease_rec->lease_addr.addr, 0));
416				return (-1);
417			}
418			iaidaddr->client6_info.iaidinfo.renewtime =
419					client6_info.iaidinfo.renewtime;
420			iaidaddr->client6_info.iaidinfo.rebindtime =
421					client6_info.iaidinfo.rebindtime;
422			iaidaddr->start_date = lease_rec->start_date;
423		}
424		if (!memcmp(&iaidaddr->client6_info, &client6_info,
425				sizeof(iaidaddr->client6_info))) {
426			/* iaidaddr->start_date != lease_rec->start_date) */
427			dprintf(LOG_DEBUG, "%s" " this lease %s client iaidinfo is different"
428				" to the previous lease",
429				FNAME,  in6addr2str(&lease_rec->lease_addr.addr, 0));
430			return (-1);
431		}
432	}
433	if (lease_rec->state == INVALID) {
434		dprintf(LOG_DEBUG, "parse an invalid state lease %s/%d in line %d",
435			in6addr2str(&lease_rec->lease_addr.addr, 0),
436			lease_rec->lease_addr.plen, num_lines);
437		free(lease_rec);
438		return (0);
439	}
440	if ((lease_rec->timer = dhcp6_add_timer(dhcp6_lease_timo, lease_rec)) == NULL) {
441		dprintf(LOG_ERR, "%s" " failed to create a timer for lease %s",
442			FNAME, in6addr2str(&lease_rec->lease_addr.addr, 0));
443			return (-1);
444	}
445	if (lease_rec->lease_addr.preferlifetime > offset) {
446		lease_rec->state = ACTIVE;
447		d = lease_rec->lease_addr.preferlifetime - offset;
448	} else {
449		lease_rec->state = EXPIRED;
450		d = lease_rec->lease_addr.validlifetime - offset;
451	}
452	timo.tv_sec = (long)d;
453	timo.tv_usec = 0;
454	dhcp6_set_timer(&timo, lease_rec->timer);
455	if (dhcp6_mode == DHCP6_MODE_SERVER) {
456		if (hash_add(lease_hash_table, &lease_rec->lease_addr, lease_rec) != 0) {
457			dprintf(LOG_ERR, "%s" "hash add lease failed for %s",
458				FNAME, in6addr2str(&lease_rec->lease_addr.addr, 0));
459			free(lease_rec);
460			return (-1);
461		}
462	}
463	lease_rec->iaidaddr = iaidaddr;
464	lease_rec->lease_addr.status_code = DH6OPT_STCODE_UNDEFINE;
465	TAILQ_INSERT_TAIL(&iaidaddr->lease_list, lease_rec, link);
466	dprintf(LOG_INFO, "%s" "add lease addr %s/%d type %d to %u",
467		FNAME, in6addr2str(&lease_rec->lease_addr.addr, 0), lease_rec->lease_addr.plen,
468		lease_rec->lease_addr.type, lease_rec->iaidaddr->client6_info.iaidinfo.iaid);
469	return (0);
470}
471
472static int
473remove_lease(lease)
474	struct dhcp6_lease *lease;
475{
476	if (dhcp6_mode == DHCP6_MODE_SERVER) {
477		if (hash_delete(lease_hash_table, &lease->lease_addr) != 0) {
478			dprintf(LOG_ERR, "%s" "failed to remove an address %s from hash",
479				FNAME, in6addr2str(&lease->lease_addr.addr, 0));
480			return (-1);
481		}
482	}
483	if (lease->timer)
484		dhcp6_remove_timer(lease->timer);
485	TAILQ_REMOVE(&lease->iaidaddr->lease_list, lease, link);
486	dprintf(LOG_INFO, "%s" "removed lease addr %s/%d from %u",
487		FNAME, in6addr2str(&lease->lease_addr.addr, 0),
488		lease->lease_addr.plen, lease->iaidaddr->client6_info.iaidinfo.iaid);
489	free(lease);
490	return 0;
491}
492