summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Obser <florian@cvs.openbsd.org>2024-06-05 16:15:48 +0000
committerFlorian Obser <florian@cvs.openbsd.org>2024-06-05 16:15:48 +0000
commit1efba8e55747560fb5165c9196a31f2f103d7dfb (patch)
tree1d1f093c0924e8e217b2205edf2a73b2de96cba3
parent32e83c6bcac92616e53b2af69fafff46b24b10e8 (diff)
Implement lease files.
-rw-r--r--sbin/dhcp6leased/Makefile7
-rw-r--r--sbin/dhcp6leased/control.c3
-rw-r--r--sbin/dhcp6leased/dhcp6leased.c122
-rw-r--r--sbin/dhcp6leased/dhcp6leased.h54
-rw-r--r--sbin/dhcp6leased/engine.c65
-rw-r--r--sbin/dhcp6leased/parse.y37
-rw-r--r--sbin/dhcp6leased/parse_lease.y289
7 files changed, 491 insertions, 86 deletions
diff --git a/sbin/dhcp6leased/Makefile b/sbin/dhcp6leased/Makefile
index 4574a9a6182..73effc96b8a 100644
--- a/sbin/dhcp6leased/Makefile
+++ b/sbin/dhcp6leased/Makefile
@@ -1,8 +1,8 @@
-# $OpenBSD: Makefile,v 1.1 2024/06/02 12:28:05 florian Exp $
+# $OpenBSD: Makefile,v 1.2 2024/06/05 16:15:47 florian Exp $
PROG= dhcp6leased
SRCS= control.c dhcp6leased.c engine.c frontend.c log.c
-SRCS+= parse.y printconf.c
+SRCS+= parse.y printconf.c parse_lease.y
MAN= dhcp6leased.8 dhcp6leased.conf.5
@@ -17,6 +17,9 @@ YFLAGS=
LDADD+= -levent -lutil
DPADD+= ${LIBEVENT} ${LIBUTIL}
+parse_lease.c: parse_lease.y
+ ${YACC.y} -ppl -o ${.TARGET} ${.IMPSRC}
+
.include <bsd.prog.mk>
# Don't compile dhcp6leased as static binary by default
diff --git a/sbin/dhcp6leased/control.c b/sbin/dhcp6leased/control.c
index 5bc63c11af5..364b9ada478 100644
--- a/sbin/dhcp6leased/control.c
+++ b/sbin/dhcp6leased/control.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: control.c,v 1.2 2024/06/02 13:35:52 florian Exp $ */
+/* $OpenBSD: control.c,v 1.3 2024/06/05 16:15:47 florian Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -28,6 +28,7 @@
#include <errno.h>
#include <event.h>
#include <imsg.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
diff --git a/sbin/dhcp6leased/dhcp6leased.c b/sbin/dhcp6leased/dhcp6leased.c
index 160ceb410c9..61fbd182a59 100644
--- a/sbin/dhcp6leased/dhcp6leased.c
+++ b/sbin/dhcp6leased/dhcp6leased.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: dhcp6leased.c,v 1.10 2024/06/05 16:11:26 florian Exp $ */
+/* $OpenBSD: dhcp6leased.c,v 1.11 2024/06/05 16:15:47 florian Exp $ */
/*
* Copyright (c) 2017, 2021, 2024 Florian Obser <florian@openbsd.org>
@@ -78,6 +78,7 @@ void configure_address(struct imsg_configure_address *);
void deconfigure_address(struct imsg_configure_address *);
void read_lease_file(struct imsg_ifinfo *);
uint8_t *get_uuid(void);
+void write_lease_file(struct imsg_lease_info *);
int main_imsg_send_ipc_sockets(struct imsgbuf *, struct imsgbuf *);
int main_imsg_compose_frontend(int, int, void *, uint16_t);
@@ -309,7 +310,8 @@ main(int argc, char *argv[])
if (unveil(NULL, NULL) == -1)
fatal("unveil");
- if (pledge("stdio inet rpath wpath sendfd wroute", NULL) == -1)
+ if (pledge("stdio inet rpath wpath cpath fattr sendfd wroute", NULL)
+ == -1)
fatal("pledge");
main_imsg_compose_frontend(IMSG_ROUTESOCK, frontend_routesock, NULL, 0);
@@ -543,6 +545,17 @@ main_dispatch_engine(int fd, short event, void *bula)
deconfigure_address(&imsg_configure_address);
break;
}
+ case IMSG_WRITE_LEASE: {
+ struct imsg_lease_info imsg_lease_info;
+ if (IMSG_DATA_SIZE(imsg) !=
+ sizeof(imsg_lease_info))
+ fatalx("%s: IMSG_WRITE_LEASE wrong length: %lu",
+ __func__, IMSG_DATA_SIZE(imsg));
+ memcpy(&imsg_lease_info, imsg.data,
+ sizeof(imsg_lease_info));
+ write_lease_file(&imsg_lease_info);
+ break;
+ }
default:
log_debug("%s: error handling imsg %d", __func__,
imsg.hdr.type);
@@ -859,16 +872,98 @@ open_udpsock(uint32_t if_index)
}
void
+write_lease_file(struct imsg_lease_info *imsg_lease_info)
+{
+ struct iface_conf *iface_conf;
+ uint32_t i;
+ int len, fd, rem;
+ char if_name[IF_NAMESIZE];
+ char lease_buf[LEASE_SIZE];
+ char lease_file_buf[sizeof(_PATH_LEASE) +
+ IF_NAMESIZE];
+ char tmpl[] = _PATH_LEASE"XXXXXXXXXX";
+ char ntopbuf[INET6_ADDRSTRLEN];
+ char *p;
+
+ if (no_lease_files)
+ return;
+
+ if (if_indextoname(imsg_lease_info->if_index, if_name) == NULL) {
+ log_warnx("%s: cannot find interface %d", __func__,
+ imsg_lease_info->if_index);
+ return;
+ }
+
+ if ((iface_conf = find_iface_conf(&main_conf->iface_list, if_name))
+ == NULL) {
+ log_debug("%s: no interface configuration for %s", __func__,
+ if_name);
+ return;
+ }
+
+ len = snprintf(lease_file_buf, sizeof(lease_file_buf), "%s%s",
+ _PATH_LEASE, if_name);
+ if (len == -1 || (size_t) len >= sizeof(lease_file_buf)) {
+ log_warnx("%s: failed to encode lease path for %s", __func__,
+ if_name);
+ return;
+ }
+
+ p = lease_buf;
+ rem = sizeof(lease_buf);
+
+ for (i = 0; i < iface_conf->ia_count; i++) {
+ len = snprintf(p, rem, "%s%d %s %d\n", LEASE_IA_PD_PREFIX,
+ i, inet_ntop(AF_INET6, &imsg_lease_info->pds[i].prefix,
+ ntopbuf, INET6_ADDRSTRLEN),
+ imsg_lease_info->pds[i].prefix_len);
+ if (len == -1 || len >= rem) {
+ log_warnx("%s: failed to encode lease for %s", __func__,
+ if_name);
+ return;
+ }
+ p += len;
+ rem -= len;
+ }
+
+ len = sizeof(lease_buf) - rem;
+
+ if ((fd = mkstemp(tmpl)) == -1) {
+ log_warn("%s: mkstemp", __func__);
+ return;
+ }
+
+ if (write(fd, lease_buf, len) < len)
+ goto err;
+
+ if (fchmod(fd, 0644) == -1)
+ goto err;
+
+ if (close(fd) == -1)
+ goto err;
+ fd = -1;
+
+ if (rename(tmpl, lease_file_buf) == -1)
+ goto err;
+ return;
+ err:
+ log_warn("%s", __func__);
+ if (fd != -1)
+ close(fd);
+ unlink(tmpl);
+}
+
+void
read_lease_file(struct imsg_ifinfo *imsg_ifinfo)
{
- int len, fd;
+ int len;
char if_name[IF_NAMESIZE];
char lease_file_buf[sizeof(_PATH_LEASE) + IF_NAMESIZE];
if (no_lease_files)
return;
- memset(imsg_ifinfo->lease, 0, sizeof(imsg_ifinfo->lease));
+ memset(imsg_ifinfo->pds, 0, sizeof(imsg_ifinfo->pds));
if (if_indextoname(imsg_ifinfo->if_index, if_name) == NULL) {
log_warnx("%s: cannot find interface %d", __func__,
@@ -883,13 +978,22 @@ read_lease_file(struct imsg_ifinfo *imsg_ifinfo)
if_name);
return;
}
+ parse_lease(lease_file_buf, imsg_ifinfo);
- if ((fd = open(lease_file_buf, O_RDONLY)) == -1)
- return;
+ if (log_getverbose() > 1) {
+ int i;
+ char ntopbuf[INET6_ADDRSTRLEN];
+
+ for (i = 0; i < MAX_IA; i++) {
+ if (imsg_ifinfo->pds[i].prefix_len == 0)
+ continue;
- /* no need for error handling, we'll just do a DHCP discover */
- read(fd, imsg_ifinfo->lease, sizeof(imsg_ifinfo->lease) - 1);
- close(fd);
+ log_debug("%s: %s: %d %s/%d", __func__, if_name, i,
+ inet_ntop(AF_INET6, &imsg_ifinfo->pds[i].prefix,
+ ntopbuf, INET6_ADDRSTRLEN),
+ imsg_ifinfo->pds[i].prefix_len);
+ }
+ }
}
void
diff --git a/sbin/dhcp6leased/dhcp6leased.h b/sbin/dhcp6leased/dhcp6leased.h
index 35f29cc2d8a..040cf8cbb6b 100644
--- a/sbin/dhcp6leased/dhcp6leased.h
+++ b/sbin/dhcp6leased/dhcp6leased.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: dhcp6leased.h,v 1.6 2024/06/05 16:14:12 florian Exp $ */
+/* $OpenBSD: dhcp6leased.h,v 1.7 2024/06/05 16:15:47 florian Exp $ */
/*
* Copyright (c) 2017, 2021 Florian Obser <florian@openbsd.org>
@@ -37,13 +37,8 @@
#define XID_SIZE 3
#define SERVERID_SIZE 130 /* 2 octet type, max 128 octets data */
#define MAX_IA 32
-#define LEASE_VERSION "version: 2"
-#define LEASE_IP_PREFIX "ip: "
-#define LEASE_NEXTSERVER_PREFIX "next-server: "
-#define LEASE_BOOTFILE_PREFIX "filename: "
-#define LEASE_HOSTNAME_PREFIX "host-name: "
-#define LEASE_DOMAIN_PREFIX "domain-name: "
#define LEASE_SIZE 4096
+#define LEASE_IA_PD_PREFIX "ia_pd "
/* MAXDNAME from arpa/namesr.h */
#define DHCP6LEASED_MAX_DNSSL 1025
#define MAX_RDNS_COUNT 8 /* max nameserver in a RTM_PROPOSAL */
@@ -177,6 +172,7 @@ enum imsg_type {
IMSG_CONFIGURE_ADDRESS,
IMSG_DECONFIGURE_ADDRESS,
IMSG_REQUEST_REBOOT,
+ IMSG_WRITE_LEASE,
};
struct ctl_engine_info {
@@ -217,12 +213,19 @@ struct dhcp6leased_conf {
int rapid_commit;
};
+struct prefix {
+ struct in6_addr prefix;
+ int prefix_len;
+ uint32_t vltime;
+ uint32_t pltime;
+};
+
struct imsg_ifinfo {
uint32_t if_index;
int rdomain;
int running;
int link_state;
- char lease[LEASE_SIZE];
+ struct prefix pds[MAX_IA];
};
struct imsg_dhcp {
@@ -231,13 +234,6 @@ struct imsg_dhcp {
uint8_t packet[1500];
};
-struct prefix {
- struct in6_addr prefix;
- int prefix_len;
- uint32_t vltime;
- uint32_t pltime;
-};
-
struct imsg_req_dhcp {
uint32_t if_index;
int elapsed_time;
@@ -247,6 +243,11 @@ struct imsg_req_dhcp {
struct prefix pds[MAX_IA];
};
+struct imsg_lease_info {
+ uint32_t if_index;
+ struct prefix pds[MAX_IA];
+};
+
/* dhcp6leased.c */
void imsg_event_add(struct imsgev *);
int imsg_compose_event(struct imsgev *, uint16_t, uint32_t,
@@ -268,6 +269,25 @@ int *changed_ifaces(struct dhcp6leased_conf *, struct
void print_config(struct dhcp6leased_conf *, int);
/* parse.y */
-struct dhcp6leased_conf *parse_config(const char *);
-int cmdline_symset(char *);
+struct file {
+ TAILQ_ENTRY(file) entry;
+ FILE *stream;
+ char *name;
+ size_t ungetpos;
+ size_t ungetsize;
+ u_char *ungetbuf;
+ int eof_reached;
+ int lineno;
+ int errors;
+};
+struct dhcp6leased_conf *parse_config(const char *);
+struct file *pushfile(const char *, int);
+int popfile(void);
+int kw_cmp(const void *, const void *);
+int lgetc(int);
+void lungetc(int);
+int findeol(void);
+
+/* parse_lease.y */
+void parse_lease(const char*, struct imsg_ifinfo *);
diff --git a/sbin/dhcp6leased/engine.c b/sbin/dhcp6leased/engine.c
index 1e0bb1f2127..6f9ccf16108 100644
--- a/sbin/dhcp6leased/engine.c
+++ b/sbin/dhcp6leased/engine.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: engine.c,v 1.11 2024/06/05 16:12:09 florian Exp $ */
+/* $OpenBSD: engine.c,v 1.12 2024/06/05 16:15:47 florian Exp $ */
/*
* Copyright (c) 2017, 2021, 2024 Florian Obser <florian@openbsd.org>
@@ -136,7 +136,7 @@ void configure_interfaces(struct dhcp6leased_iface *);
void deconfigure_interfaces(struct dhcp6leased_iface *);
void send_reconfigure_interface(struct iface_pd_conf *,
struct prefix *, enum reconfigure_action);
-void parse_lease(struct dhcp6leased_iface *,
+void parse_lease_xxx(struct dhcp6leased_iface *,
struct imsg_ifinfo *);
int engine_imsg_compose_main(int, pid_t, void *, uint16_t);
const char *dhcp_message_type2str(uint8_t);
@@ -445,8 +445,6 @@ engine_dispatch_main(int fd, short event, void *bula)
fatalx("%s: IMSG_UPDATE_IF wrong length: %lu",
__func__, IMSG_DATA_SIZE(imsg));
memcpy(&imsg_ifinfo, imsg.data, sizeof(imsg_ifinfo));
- if (imsg_ifinfo.lease[LEASE_SIZE - 1] != '\0')
- fatalx("Invalid lease");
engine_update_iface(&imsg_ifinfo);
break;
case IMSG_RECONF_CONF:
@@ -601,7 +599,9 @@ void
engine_update_iface(struct imsg_ifinfo *imsg_ifinfo)
{
struct dhcp6leased_iface *iface;
- int need_refresh = 0;
+ struct iface_conf *iface_conf;
+ int need_refresh = 0;
+ char ifnamebuf[IF_NAMESIZE], *if_name;
iface = get_dhcp6leased_iface_by_id(imsg_ifinfo->if_index);
@@ -637,18 +637,38 @@ engine_update_iface(struct imsg_ifinfo *imsg_ifinfo)
if (!need_refresh)
return;
+ if ((if_name = if_indextoname(iface->if_index, ifnamebuf)) == NULL) {
+ log_debug("%s: unknown interface %d", __func__,
+ iface->if_index);
+ return;
+ }
+
+ if ((iface_conf = find_iface_conf(&engine_conf->iface_list, if_name))
+ == NULL) {
+ log_debug("%s: no interface configuration for %d", __func__,
+ iface->if_index);
+ return;
+ }
+
if (iface->running && LINK_STATE_IS_UP(iface->link_state)) {
-#if 0
-XXXX
- if (iface->requested_ip.s_addr == INADDR_ANY)
- parse_lease(iface, imsg_ifinfo);
+ uint32_t i;
+ int got_lease;
- if (iface->requested_ip.s_addr == INADDR_ANY)
- state_transition(iface, IF_INIT);
- else
+ if (iface->pds[0].prefix_len == 0)
+ memcpy(iface->pds, imsg_ifinfo->pds,
+ sizeof(iface->pds));
+
+ got_lease = 0;
+ for (i = 0; i < iface_conf->ia_count; i++) {
+ if (iface->pds[i].prefix_len > 0) {
+ got_lease = 1;
+ break;
+ }
+ }
+ if (got_lease)
state_transition(iface, IF_REBOOTING);
-#endif
- state_transition(iface, IF_INIT);
+ else
+ state_transition(iface, IF_INIT);
} else
state_transition(iface, IF_DOWN);
}
@@ -884,6 +904,7 @@ parse_dhcp(struct dhcp6leased_iface *iface, struct imsg_dhcp *dhcp)
case IF_REQUESTING:
case IF_RENEWING:
case IF_REBINDING:
+ case IF_REBOOTING:
break;
case IF_INIT:
if (rapid_commit && engine_conf->rapid_commit)
@@ -1232,8 +1253,6 @@ request_dhcp_request(struct dhcp6leased_iface *iface)
fatalx("invalid state IF_BOUND in %s", __func__);
break;
case IF_REBOOTING:
- fatalx("XXX state IF_REBOOTING in %s not IMPL", __func__);
- break;
case IF_REQUESTING:
case IF_RENEWING:
case IF_REBINDING:
@@ -1251,6 +1270,7 @@ request_dhcp_request(struct dhcp6leased_iface *iface)
engine_imsg_compose_frontend(IMSG_SEND_RENEW, 0, &imsg,
sizeof(imsg));
break;
+ case IF_REBOOTING:
case IF_REBINDING:
engine_imsg_compose_frontend(IMSG_SEND_REBIND, 0, &imsg,
sizeof(imsg));
@@ -1267,6 +1287,7 @@ configure_interfaces(struct dhcp6leased_iface *iface)
struct iface_conf *iface_conf;
struct iface_ia_conf *ia_conf;
struct iface_pd_conf *pd_conf;
+ struct imsg_lease_info imsg_lease_info;
char ifnamebuf[IF_NAMESIZE], *if_name;
@@ -1282,6 +1303,12 @@ configure_interfaces(struct dhcp6leased_iface *iface)
return;
}
+ memset(&imsg_lease_info, 0, sizeof(imsg_lease_info));
+ imsg_lease_info.if_index = iface->if_index;
+ memcpy(imsg_lease_info.pds, iface->pds, sizeof(iface->pds));
+ engine_imsg_compose_main(IMSG_WRITE_LEASE, 0, &imsg_lease_info,
+ sizeof(imsg_lease_info));
+
SIMPLEQ_FOREACH(ia_conf, &iface_conf->iface_ia_list, entry) {
struct prefix *pd = &iface->pds[ia_conf->id];
@@ -1371,12 +1398,6 @@ send_reconfigure_interface(struct iface_pd_conf *pd_conf, struct prefix *pd,
sizeof(address));
}
-void
-parse_lease(struct dhcp6leased_iface *iface, struct imsg_ifinfo *imsg_ifinfo)
-{
- fatalx("%s: not implemented", __func__); /* XXX */
-}
-
const char *
dhcp_message_type2str(uint8_t type)
{
diff --git a/sbin/dhcp6leased/parse.y b/sbin/dhcp6leased/parse.y
index 940ec15d1f8..3b60e7726a6 100644
--- a/sbin/dhcp6leased/parse.y
+++ b/sbin/dhcp6leased/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.7 2024/06/04 15:48:47 florian Exp $ */
+/* $OpenBSD: parse.y,v 1.8 2024/06/05 16:15:47 florian Exp $ */
/*
* Copyright (c) 2018, 2024 Florian Obser <florian@openbsd.org>
@@ -52,31 +52,15 @@
#include "frontend.h"
TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
-static struct file {
- TAILQ_ENTRY(file) entry;
- FILE *stream;
- char *name;
- size_t ungetpos;
- size_t ungetsize;
- u_char *ungetbuf;
- int eof_reached;
- int lineno;
- int errors;
-} *file, *topfile;
-struct file *pushfile(const char *, int);
-int popfile(void);
+struct file *file, *topfile;
int check_file_secrecy(int, const char *);
int yyparse(void);
int yylex(void);
int yyerror(const char *, ...)
__attribute__((__format__ (printf, 1, 2)))
__attribute__((__nonnull__ (1)));
-int kw_cmp(const void *, const void *);
int lookup(char *);
int igetc(void);
-int lgetc(int);
-void lungetc(int);
-int findeol(void);
TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
struct sym {
@@ -699,23 +683,6 @@ symset(const char *nam, const char *val, int persist)
return (0);
}
-int
-cmdline_symset(char *s)
-{
- char *sym, *val;
- int ret;
-
- if ((val = strrchr(s, '=')) == NULL)
- return (-1);
- sym = strndup(s, val - s);
- if (sym == NULL)
- errx(1, "%s: strndup", __func__);
- ret = symset(sym, val + 1, 1);
- free(sym);
-
- return (ret);
-}
-
char *
symget(const char *nam)
{
diff --git a/sbin/dhcp6leased/parse_lease.y b/sbin/dhcp6leased/parse_lease.y
new file mode 100644
index 00000000000..767e0de8ff2
--- /dev/null
+++ b/sbin/dhcp6leased/parse_lease.y
@@ -0,0 +1,289 @@
+/* $OpenBSD: parse_lease.y,v 1.1 2024/06/05 16:15:47 florian Exp $ */
+
+/*
+ * Copyright (c) 2018, 2024 Florian Obser <florian@openbsd.org>
+ * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
+ * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2001 Markus Friedl. All rights reserved.
+ * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
+ * Copyright (c) 2001 Theo de Raadt. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+%{
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <event.h>
+#include <imsg.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <vis.h>
+
+#include "log.h"
+#include "dhcp6leased.h"
+#include "frontend.h"
+
+extern TAILQ_HEAD(files, file) files;
+extern struct file *file, *topfile;
+int yyparse(void);
+int yylex(void);
+int yyerror(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)))
+ __attribute__((__nonnull__ (1)));
+int pllookup(char *);
+
+struct imsg_ifinfo *ifinfo;
+static int errors;
+
+typedef struct {
+ union {
+ int64_t number;
+ char *string;
+ } v;
+ int lineno;
+} YYSTYPE;
+
+%}
+
+%token ERROR IAPD
+
+%token <v.string> STRING
+%token <v.number> NUMBER
+
+%%
+
+grammar : /* empty */
+ | grammar '\n'
+ | grammar ia_pd '\n'
+ | grammar error '\n' { file->errors++; }
+ ;
+
+ia_pd : IAPD NUMBER STRING NUMBER {
+ if ($2 < 0 || $2 > MAX_IA) {
+ yyerror("invalid IA_ID %lld", $2);
+ free($3);
+ YYERROR;
+ }
+ if ($4 < 1 || $4 > 128) {
+ yyerror("invalid prefix length %lld", $4);
+ free($3);
+ ifinfo->pds[$2].prefix_len = 0;
+ YYERROR;
+ } else
+ ifinfo->pds[$2].prefix_len = $4;
+
+ if (inet_pton(AF_INET6, $3, &ifinfo->pds[$2].prefix)
+ != 1) {
+ yyerror("invalid prefix %s", $3);
+ free($3);
+ ifinfo->pds[$2].prefix_len = 0;
+ YYERROR;
+ }
+ free($3);
+ }
+ ;
+%%
+
+struct keywords {
+ const char *k_name;
+ int k_val;
+};
+
+int
+yyerror(const char *fmt, ...)
+{
+ va_list ap;
+ char *msg;
+
+ file->errors++;
+ va_start(ap, fmt);
+ if (vasprintf(&msg, fmt, ap) == -1)
+ fatalx("yyerror vasprintf");
+ va_end(ap);
+ logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
+ free(msg);
+ return (0);
+}
+
+int
+pllookup(char *s)
+{
+ /* This has to be sorted always. */
+ static const struct keywords keywords[] = {
+ {"ia_pd", IAPD},
+ };
+ const struct keywords *p;
+
+ p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
+ sizeof(keywords[0]), kw_cmp);
+
+ if (p)
+ return (p->k_val);
+ else
+ return (STRING);
+}
+
+int
+yylex(void)
+{
+ char buf[8096];
+ char *p;
+ int quotec, next, c;
+ int token;
+
+ p = buf;
+ while ((c = lgetc(0)) == ' ' || c == '\t')
+ ; /* nothing */
+
+ yylval.lineno = file->lineno;
+ if (c == '#')
+ while ((c = lgetc(0)) != '\n' && c != EOF)
+ ; /* nothing */
+ switch (c) {
+ case '\'':
+ case '"':
+ quotec = c;
+ while (1) {
+ if ((c = lgetc(quotec)) == EOF)
+ return (0);
+ if (c == '\n') {
+ file->lineno++;
+ continue;
+ } else if (c == '\\') {
+ if ((next = lgetc(quotec)) == EOF)
+ return (0);
+ if (next == quotec || next == ' ' ||
+ next == '\t')
+ c = next;
+ else if (next == '\n') {
+ file->lineno++;
+ continue;
+ } else
+ lungetc(next);
+ } else if (c == quotec) {
+ *p = '\0';
+ break;
+ } else if (c == '\0') {
+ yyerror("syntax error");
+ return (findeol());
+ }
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ *p++ = c;
+ }
+ yylval.v.string = strdup(buf);
+ if (yylval.v.string == NULL)
+ err(1, "yylex: strdup");
+ return (STRING);
+ }
+
+#define allowed_to_end_number(x) \
+ (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
+
+ if (c == '-' || isdigit(c)) {
+ do {
+ *p++ = c;
+ if ((size_t)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ } while ((c = lgetc(0)) != EOF && isdigit(c));
+ lungetc(c);
+ if (p == buf + 1 && buf[0] == '-')
+ goto nodigits;
+ if (c == EOF || allowed_to_end_number(c)) {
+ const char *errstr = NULL;
+
+ *p = '\0';
+ yylval.v.number = strtonum(buf, LLONG_MIN,
+ LLONG_MAX, &errstr);
+ if (errstr) {
+ yyerror("\"%s\" invalid number: %s",
+ buf, errstr);
+ return (findeol());
+ }
+ return (NUMBER);
+ } else {
+nodigits:
+ while (p > buf + 1)
+ lungetc((unsigned char)*--p);
+ c = (unsigned char)*--p;
+ if (c == '-')
+ return (c);
+ }
+ }
+
+#define allowed_in_string(x) \
+ (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
+ x != '{' && x != '}' && \
+ x != '!' && x != '=' && x != '#' && \
+ x != ','))
+
+ if (isalnum(c) || c == ':' || c == '_') {
+ do {
+ *p++ = c;
+ if ((size_t)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
+ lungetc(c);
+ *p = '\0';
+ if ((token = pllookup(buf)) == STRING)
+ if ((yylval.v.string = strdup(buf)) == NULL)
+ err(1, "yylex: strdup");
+ return (token);
+ }
+ if (c == '\n') {
+ yylval.lineno = file->lineno;
+ file->lineno++;
+ }
+ if (c == EOF)
+ return (0);
+ return (c);
+}
+
+void
+parse_lease(const char *filename, struct imsg_ifinfo *imsg)
+{
+ ifinfo = imsg;
+ file = pushfile(filename, 0);
+ if (file == NULL)
+ return;
+
+ topfile = file;
+
+ yyparse();
+ errors = file->errors;
+ popfile();
+}