-# $OpenBSD: Makefile,v 1.1 2021/02/26 16:16:37 florian Exp $
+# $OpenBSD: Makefile,v 1.2 2021/07/26 09:26:36 florian Exp $
PROG= dhcpleased
SRCS= bpf.c checksum.c control.c dhcpleased.c engine.c frontend.c log.c
+SRCS+= parse.y printconf.c
-MAN= dhcpleased.8
+MAN= dhcpleased.8 dhcpleased.conf.5
#DEBUG= -g -DDEBUG=3 -O0
-/* $OpenBSD: control.c,v 1.2 2021/03/02 04:10:07 jsg Exp $ */
+/* $OpenBSD: control.c,v 1.3 2021/07/26 09:26:36 florian Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
break;
switch (imsg.hdr.type) {
+ case IMSG_CTL_RELOAD:
+ frontend_imsg_compose_main(imsg.hdr.type, 0, NULL, 0);
+ break;
case IMSG_CTL_LOG_VERBOSE:
if (IMSG_DATA_SIZE(imsg) != sizeof(verbose))
break;
-.\" $OpenBSD: dhcpleased.8,v 1.2 2021/02/26 17:14:25 tb Exp $
+.\" $OpenBSD: dhcpleased.8,v 1.3 2021/07/26 09:26:36 florian Exp $
.\"
.\" Copyright (c) 2021 Florian Obser <florian@openbsd.org>
.\"
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: February 26 2021 $
+.Dd $Mdocdate: July 26 2021 $
.Dt DHCPLEASED 8
.Os
.Sh NAME
.Nd a dynamic host configuration protocol daemon
.Sh SYNOPSIS
.Nm
-.Op Fl dv
+.Op Fl dnv
+.Op Fl f Ar file
.Op Fl s Ar socket
.Sh DESCRIPTION
.Nm
.Nm
will run in the foreground and log to
.Em stderr .
+.It Fl f Ar file
+Specify an alternative configuration file.
+.It Fl n
+Configtest mode.
+Only check the configuration file for validity.
.It Fl s Ar socket
Use an alternate location for the default control socket.
.It Fl v
.Ux Ns -domain
socket used for communication with
.Xr dhcpleasectl 8 .
+.It Pa /etc/dhcpleased.conf
+Default
+.Nm
+configuration file.
.El
.Sh SEE ALSO
.Xr hostname.if 5 ,
-/* $OpenBSD: dhcpleased.c,v 1.17 2021/07/26 09:22:00 florian Exp $ */
+/* $OpenBSD: dhcpleased.c,v 1.18 2021/07/26 09:26:36 florian Exp $ */
/*
* Copyright (c) 2017, 2021 Florian Obser <florian@openbsd.org>
int main_imsg_compose_frontend(int, int, void *, uint16_t);
int main_imsg_compose_engine(int, int, void *, uint16_t);
+#ifndef SMALL
+int main_imsg_send_config(struct dhcpleased_conf *);
+#endif /* SMALL */
+int main_reload(void);
+
static struct imsgev *iev_frontend;
static struct imsgev *iev_engine;
+#ifndef SMALL
+struct dhcpleased_conf *main_conf;
+#endif
+char *conffile;
pid_t frontend_pid;
pid_t engine_pid;
case SIGTERM:
case SIGINT:
main_shutdown();
+ case SIGHUP:
+#ifndef SMALL
+ if (main_reload() == -1)
+ log_warnx("configuration reload failed");
+ else
+ log_debug("configuration reloaded");
+#endif /* SMALL */
+ break;
default:
fatalx("unexpected signal");
}
{
extern char *__progname;
- fprintf(stderr, "usage: %s [-dv] [-s socket]\n",
+ fprintf(stderr, "usage: %s [-dnv] [-f file] [-s socket]\n",
__progname);
exit(1);
}
int
main(int argc, char *argv[])
{
- struct event ev_sigint, ev_sigterm;
+ struct event ev_sigint, ev_sigterm, ev_sighup;
int ch;
int debug = 0, engine_flag = 0, frontend_flag = 0;
- int verbose = 0;
+ int verbose = 0, no_action = 0;
char *saved_argv0;
int pipe_main2frontend[2];
int pipe_main2engine[2];
if (saved_argv0 == NULL)
saved_argv0 = "dhcpleased";
- while ((ch = getopt(argc, argv, "dEFs:v")) != -1) {
+ while ((ch = getopt(argc, argv, "dEFf:ns:v")) != -1) {
switch (ch) {
case 'd':
debug = 1;
case 'F':
frontend_flag = 1;
break;
+ case 'f':
+ conffile = optarg;
+ break;
+ case 'n':
+ no_action = 1;
+ break;
case 's':
csock = optarg;
break;
else if (frontend_flag)
frontend(debug, verbose);
+#ifndef SMALL
+ /* parse config file */
+ if ((main_conf = parse_config(conffile)) == NULL)
+ exit(1);
+
+ if (no_action) {
+ if (verbose)
+ print_config(main_conf);
+ else
+ fprintf(stderr, "configuration OK\n");
+ exit(0);
+ }
+#endif /* SMALL */
+
/* Check for root privileges. */
if (geteuid())
errx(1, "need root privileges");
/* Setup signal handler. */
signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL);
signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL);
+ signal_set(&ev_sighup, SIGHUP, main_sig_handler, NULL);
signal_add(&ev_sigint, NULL);
signal_add(&ev_sigterm, NULL);
+ signal_add(&ev_sighup, NULL);
signal(SIGPIPE, SIG_IGN);
- signal(SIGHUP, SIG_IGN);
/* Setup pipes to children. */
warnx("control socket setup failed");
#endif /* SMALL */
+ if (conffile != NULL) {
+ if (unveil(conffile, "r") == -1)
+ fatal("unveil %s", conffile);
+ } else {
+ if (unveil(_PATH_CONF_FILE, "r") == -1)
+ fatal("unveil %s", _PATH_CONF_FILE);
+ }
if (unveil("/dev/bpf", "rw") == -1)
fatal("unveil /dev/bpf");
#ifndef SMALL
if (control_fd != -1)
main_imsg_compose_frontend(IMSG_CONTROLFD, control_fd, NULL, 0);
+ main_imsg_send_config(main_conf);
#endif /* SMALL */
main_imsg_compose_frontend(IMSG_STARTUP, -1, NULL, 0);
msgbuf_clear(&iev_engine->ibuf.w);
close(iev_engine->ibuf.fd);
+#ifndef SMALL
+ config_clear(main_conf);
+#endif /* SMALL */
+
log_debug("waiting for children to terminate");
do {
pid = wait(&status);
open_bpfsock(if_index);
break;
#ifndef SMALL
+ case IMSG_CTL_RELOAD:
+ if (main_reload() == -1)
+ log_warnx("configuration reload failed");
+ else
+ log_warnx("configuration reloaded");
+ break;
case IMSG_CTL_LOG_VERBOSE:
if (IMSG_DATA_SIZE(imsg) != sizeof(verbose))
fatalx("%s: IMSG_CTL_LOG_VERBOSE wrong length: "
return (0);
}
+#ifndef SMALL
+int
+main_reload(void)
+{
+ struct dhcpleased_conf *xconf;
+
+ if ((xconf = parse_config(conffile)) == NULL)
+ return (-1);
+
+ if (main_imsg_send_config(xconf) == -1)
+ return (-1);
+
+ merge_config(main_conf, xconf);
+
+ return (0);
+}
+
+int
+main_imsg_send_config(struct dhcpleased_conf *xconf)
+{
+ struct iface_conf *iface_conf;
+
+ main_imsg_compose_frontend(IMSG_RECONF_CONF, -1, NULL, 0);
+ main_imsg_compose_engine(IMSG_RECONF_CONF, -1, NULL, 0);
+
+ /* Send the interface list to the frontend & engine. */
+ SIMPLEQ_FOREACH(iface_conf, &xconf->iface_list, entry) {
+ main_imsg_compose_frontend(IMSG_RECONF_IFACE, -1, iface_conf,
+ sizeof(*iface_conf));
+ main_imsg_compose_engine(IMSG_RECONF_IFACE, -1, iface_conf,
+ sizeof(*iface_conf));
+ main_imsg_compose_frontend(IMSG_RECONF_VC_ID, -1,
+ iface_conf->vc_id, iface_conf->vc_id_len);
+ main_imsg_compose_engine(IMSG_RECONF_VC_ID, -1,
+ iface_conf->vc_id, iface_conf->vc_id_len);
+ main_imsg_compose_frontend(IMSG_RECONF_C_ID, -1,
+ iface_conf->c_id, iface_conf->c_id_len);
+ main_imsg_compose_engine(IMSG_RECONF_C_ID, -1,
+ iface_conf->c_id, iface_conf->c_id_len);
+ }
+
+ /* Config is now complete. */
+ main_imsg_compose_frontend(IMSG_RECONF_END, -1, NULL, 0);
+ main_imsg_compose_engine(IMSG_RECONF_END, -1, NULL, 0);
+
+ return (0);
+}
+#endif /* SMALL */
+
void
configure_interface(struct imsg_configure_interface *imsg)
{
read(fd, imsg_ifinfo->lease, sizeof(imsg_ifinfo->lease) - 1);
close(fd);
}
+
+#ifndef SMALL
+void
+merge_config(struct dhcpleased_conf *conf, struct dhcpleased_conf *xconf)
+{
+ struct iface_conf *iface_conf;
+
+ /* Remove & discard existing interfaces. */
+ while ((iface_conf = SIMPLEQ_FIRST(&conf->iface_list)) != NULL) {
+ SIMPLEQ_REMOVE_HEAD(&conf->iface_list, entry);
+ free(iface_conf->vc_id);
+ free(iface_conf->c_id);
+ free(iface_conf);
+ }
+
+ /* Add new interfaces. */
+ SIMPLEQ_CONCAT(&conf->iface_list, &xconf->iface_list);
+
+ free(xconf);
+}
+
+struct dhcpleased_conf *
+config_new_empty(void)
+{
+ struct dhcpleased_conf *xconf;
+
+ xconf = calloc(1, sizeof(*xconf));
+ if (xconf == NULL)
+ fatal(NULL);
+
+ SIMPLEQ_INIT(&xconf->iface_list);
+
+ return (xconf);
+}
+
+void
+config_clear(struct dhcpleased_conf *conf)
+{
+ struct dhcpleased_conf *xconf;
+
+ /* Merge current config with an empty config. */
+ xconf = config_new_empty();
+ merge_config(conf, xconf);
+
+ free(conf);
+}
+#endif /* SMALL */
--- /dev/null
+.\" $OpenBSD: dhcpleased.conf.5,v 1.1 2021/07/26 09:26:36 florian Exp $
+.\"
+.\" Copyright (c) 2018, 2021 Florian Obser <florian@openbsd.org>
+.\" Copyright (c) 2005 Esben Norby <norby@openbsd.org>
+.\" Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org>
+.\" Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+.\" Copyright (c) 2002 Daniel Hartmeier <dhartmei@openbsd.org>
+.\"
+.\" 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.
+.\"
+.Dd $Mdocdate: July 26 2021 $
+.Dt DHCPLEASED.CONF 5
+.Os
+.Sh NAME
+.Nm dhcpleased.conf
+.Nd dynamic host configuration protocol client daemon configuration file
+.Sh DESCRIPTION
+The
+.Xr dhcpleased 8
+daemon is a dynamic host configuration protocol client daemon.
+.Pp
+The
+.Nm
+config file is divided into the following main sections:
+.Bl -tag -width xxxx
+.It Sy Macros
+User-defined variables may be defined and used later, simplifying the
+configuration file.
+.It Sy Interfaces
+This section defines interfaces for which default options need to be overwritten.
+.El
+.Sh MACROS
+Macros can be defined that will later be expanded in context.
+Macro names must start with a letter, digit, or underscore,
+and may contain any of those characters.
+Macro names may not be reserved words (for example,
+.Ic interface )
+Macros are not expanded inside quotes.
+.Sh INTERFACES
+A list of interfaces to overwrite defaults:
+.Bd -unfilled -offset indent
+.Ic interface Ar name { Oo option list Oc }
+.Ed
+.Pp
+.Ic interface
+options are as follows:
+.Bl -tag -width Ds
+.It Ic send client id Ar client-id
+Send the dhcp client identifier option with a value of
+.Ar client-id .
+If
+.Ar client-id
+consists of a series of octets of two-digit hexadecimal numbers separated by
+colons the first octet is used as the type and the rest as value.
+The MAC address 00:53:FF:AA:BB:CC would be configured as
+.Bd -literal -offset indent
+send client id "01:00:53:FF:AA:BB:CC"
+.Ed
+.Pp
+Otherwise the string
+.Ar client-id
+is send verbatim with type zero.
+The default is to send the interface's MAC address as client identifier.
+.It Ic send vendor class id Ar vendor-class-id
+Send the dhcp vendor class identifier option with a value of
+.Ar vendor-class-id .
+The default is to not send a vendor class identifier.
+.El
+.Sh FILES
+.Bl -tag -width /etc/dhcpleased.conf -compact
+.It Pa /etc/dhcpleased.conf
+.Xr dhcpleased 8
+configuration file.
+.El
+.Sh SEE ALSO
+.Xr dhcpleasectl 8 ,
+.Xr dhcpleased 8 ,
-/* $OpenBSD: dhcpleased.h,v 1.7 2021/07/21 03:53:50 kn Exp $ */
+/* $OpenBSD: dhcpleased.h,v 1.8 2021/07/26 09:26:36 florian Exp $ */
/*
* Copyright (c) 2017, 2021 Florian Obser <florian@openbsd.org>
*/
#define _PATH_LOCKFILE "/dev/dhcpleased.lock"
+#define _PATH_CONF_FILE "/etc/dhcpleased.conf"
#define _PATH_DHCPLEASED_SOCKET "/dev/dhcpleased.sock"
#define DHCPLEASED_USER "_dhcp"
#define DHCPLEASED_RTA_LABEL "dhcpleased"
#define DHCP_COOKIE {99, 130, 83, 99}
/* Possible values for hardware type (htype) field. */
+#define HTYPE_NONE 0
#define HTYPE_ETHER 1
#define HTYPE_IPSEC_TUNNEL 31
IMSG_CTL_LOG_VERBOSE,
IMSG_CTL_SHOW_INTERFACE_INFO,
IMSG_CTL_SEND_REQUEST,
+ IMSG_CTL_RELOAD,
IMSG_CTL_END,
+ IMSG_RECONF_CONF,
+ IMSG_RECONF_IFACE,
+ IMSG_RECONF_VC_ID,
+ IMSG_RECONF_C_ID,
+ IMSG_RECONF_END,
#endif /* SMALL */
IMSG_SEND_DISCOVER,
IMSG_SEND_REQUEST,
uint32_t rebinding_time;
};
+struct iface_conf {
+ SIMPLEQ_ENTRY(iface_conf) entry;
+ char name[IF_NAMESIZE];
+ uint8_t *vc_id;
+ int vc_id_len;
+ uint8_t *c_id;
+ int c_id_len;
+};
+
+struct dhcpleased_conf {
+ SIMPLEQ_HEAD(iface_conf_head, iface_conf) iface_list;
+};
+
#endif /* SMALL */
struct imsg_ifinfo {
};
/* dhcpleased.c */
-void imsg_event_add(struct imsgev *);
-int imsg_compose_event(struct imsgev *, uint16_t, uint32_t, pid_t,
- int, void *, uint16_t);
+void imsg_event_add(struct imsgev *);
+int imsg_compose_event(struct imsgev *, uint16_t, uint32_t,
+ pid_t, int, void *, uint16_t);
#ifndef SMALL
+void config_clear(struct dhcpleased_conf *);
+struct dhcpleased_conf *config_new_empty(void);
+void merge_config(struct dhcpleased_conf *, struct
+ dhcpleased_conf *);
const char *sin_to_str(struct sockaddr_in *);
+
+/* frontend.c */
+struct iface_conf *find_iface_conf(struct iface_conf_head *, char *);
+
+/* printconf.c */
+void print_config(struct dhcpleased_conf *);
+
+/* parse.y */
+struct dhcpleased_conf *parse_config(char *);
+int cmdline_symset(char *);
#else
#define sin_to_str(x...) ""
#endif /* SMALL */
+
-/* $OpenBSD: engine.c,v 1.21 2021/07/25 12:35:58 florian Exp $ */
+/* $OpenBSD: engine.c,v 1.22 2021/07/26 09:26:36 florian Exp $ */
/*
* Copyright (c) 2017, 2021 Florian Obser <florian@openbsd.org>
void log_dhcp_hdr(struct dhcp_hdr *);
const char *dhcp_message_type2str(uint8_t);
+#ifndef SMALL
+struct dhcpleased_conf *engine_conf;
+#endif /* SMALL */
+
static struct imsgev *iev_frontend;
static struct imsgev *iev_main;
int64_t proposal_id;
struct event ev_sigint, ev_sigterm;
struct passwd *pw;
+#ifndef SMALL
+ engine_conf = config_new_empty();
+#endif /* SMALL */
+
log_init(debug, LOG_DAEMON);
log_setverbose(verbose);
void
engine_dispatch_main(int fd, short event, void *bula)
{
- struct imsg imsg;
- struct imsgev *iev = bula;
- struct imsgbuf *ibuf = &iev->ibuf;
- struct imsg_ifinfo imsg_ifinfo;
- ssize_t n;
- int shut = 0;
+#ifndef SMALL
+ static struct dhcpleased_conf *nconf;
+ static struct iface_conf *iface_conf;
+#endif /* SMALL */
+ struct imsg imsg;
+ struct imsgev *iev = bula;
+ struct imsgbuf *ibuf = &iev->ibuf;
+ struct imsg_ifinfo imsg_ifinfo;
+ ssize_t n;
+ int shut = 0;
if (event & EV_READ) {
if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
memcpy(&imsg_ifinfo, imsg.data, sizeof(imsg_ifinfo));
engine_update_iface(&imsg_ifinfo);
break;
+#ifndef SMALL
+ case IMSG_RECONF_CONF:
+ if (nconf != NULL)
+ fatalx("%s: IMSG_RECONF_CONF already in "
+ "progress", __func__);
+ if ((nconf = malloc(sizeof(struct dhcpleased_conf))) ==
+ NULL)
+ fatal(NULL);
+ SIMPLEQ_INIT(&nconf->iface_list);
+ break;
+ case IMSG_RECONF_IFACE:
+ if (IMSG_DATA_SIZE(imsg) != sizeof(struct
+ iface_conf))
+ fatalx("%s: IMSG_RECONF_IFACE wrong length: "
+ "%lu", __func__, IMSG_DATA_SIZE(imsg));
+ if ((iface_conf = malloc(sizeof(struct iface_conf)))
+ == NULL)
+ fatal(NULL);
+ memcpy(iface_conf, imsg.data, sizeof(struct
+ iface_conf));
+ iface_conf->vc_id = NULL;
+ iface_conf->vc_id_len = 0;
+ iface_conf->c_id = NULL;
+ iface_conf->c_id_len = 0;
+ SIMPLEQ_INSERT_TAIL(&nconf->iface_list,
+ iface_conf, entry);
+ break;
+ case IMSG_RECONF_VC_ID:
+ if (iface_conf == NULL)
+ fatal("IMSG_RECONF_VC_ID without "
+ "IMSG_RECONF_IFACE");
+ if (IMSG_DATA_SIZE(imsg) > 255 + 2)
+ fatalx("%s: IMSG_RECONF_VC_ID wrong length: "
+ "%lu", __func__, IMSG_DATA_SIZE(imsg));
+ if ((iface_conf->vc_id = malloc(IMSG_DATA_SIZE(imsg)))
+ == NULL)
+ fatal(NULL);
+ memcpy(iface_conf->vc_id, imsg.data,
+ IMSG_DATA_SIZE(imsg));
+ iface_conf->vc_id_len = IMSG_DATA_SIZE(imsg);
+ break;
+ case IMSG_RECONF_C_ID:
+ if (iface_conf == NULL)
+ fatal("IMSG_RECONF_C_ID without "
+ "IMSG_RECONF_IFACE");
+ if (IMSG_DATA_SIZE(imsg) > 255 + 2)
+ fatalx("%s: IMSG_RECONF_C_ID wrong length: "
+ "%lu", __func__, IMSG_DATA_SIZE(imsg));
+ if ((iface_conf->c_id = malloc(IMSG_DATA_SIZE(imsg)))
+ == NULL)
+ fatal(NULL);
+ memcpy(iface_conf->c_id, imsg.data,
+ IMSG_DATA_SIZE(imsg));
+ iface_conf->c_id_len = IMSG_DATA_SIZE(imsg);
+ break;
+ case IMSG_RECONF_END:
+ if (nconf == NULL)
+ fatalx("%s: IMSG_RECONF_END without "
+ "IMSG_RECONF_CONF", __func__);
+ merge_config(engine_conf, nconf);
+ nconf = NULL;
+ break;
+#endif /* SMALL */
default:
log_debug("%s: unexpected imsg %d", __func__,
imsg.hdr.type);
{
static uint8_t cookie[] = DHCP_COOKIE;
static struct ether_addr bcast_mac;
+#ifndef SMALL
+ struct iface_conf *iface_conf;
+#endif /* SMALL */
struct ether_header *eh;
struct ether_addr ether_src, ether_dst;
struct ip *ip;
if (bcast_mac.ether_addr_octet[0] == 0)
memset(bcast_mac.ether_addr_octet, 0xff, ETHER_ADDR_LEN);
+ if_name = if_indextoname(iface->if_index, ifnamebuf);
+
+#ifndef SMALL
+ iface_conf = find_iface_conf(&engine_conf->iface_list, if_name);
+#endif /* SMALL*/
+
memset(hbuf_src, 0, sizeof(hbuf_src));
memset(hbuf_dst, 0, sizeof(hbuf_dst));
break;
case DHO_DHCP_CLIENT_IDENTIFIER:
/* the server is supposed to echo this back to us */
- if (dho_len != 1 + sizeof(iface->hw_address))
- goto wrong_length;
- if (*p != HTYPE_ETHER) {
- log_warn("DHO_DHCP_CLIENT_IDENTIFIER: wrong "
- "type");
- return;
- }
- if (memcmp(p + 1, &iface->hw_address,
- sizeof(iface->hw_address)) != 0) {
- log_warn("wrong DHO_DHCP_CLIENT_IDENTIFIER");
- return;
+#ifndef SMALL
+ if (iface_conf != NULL && iface_conf->c_id_len > 0) {
+ if (dho_len != iface_conf->c_id[1]) {
+ log_warnx("wrong "
+ "DHO_DHCP_CLIENT_IDENTIFIER");
+ return;
+ }
+ if (memcmp(p, &iface_conf->c_id[2], dho_len) !=
+ 0) {
+ log_warnx("wrong "
+ "DHO_DHCP_CLIENT_IDENTIFIER");
+ return;
+ }
+ } else
+#endif /* SMALL */
+ {
+ if (dho_len != 1 + sizeof(iface->hw_address))
+ goto wrong_length;
+ if (*p != HTYPE_ETHER) {
+ log_warnx("DHO_DHCP_CLIENT_IDENTIFIER: "
+ "wrong type");
+ return;
+ }
+ if (memcmp(p + 1, &iface->hw_address,
+ sizeof(iface->hw_address)) != 0) {
+ log_warnx("wrong "
+ "DHO_DHCP_CLIENT_IDENTIFIER");
+ return;
+ }
}
p += dho_len;
rem -= dho_len;
log_warnx("%s: %lu bytes garbage data from %s", __func__, rem,
from);
- if_name = if_indextoname(iface->if_index, ifnamebuf);
log_debug("%s on %s from %s/%s to %s/%s",
dhcp_message_type2str(dhcp_message_type), if_name == NULL ? "?" :
if_name, from, hbuf_src, to, hbuf_dst);
-/* $OpenBSD: frontend.c,v 1.13 2021/07/12 15:09:18 beck Exp $ */
+/* $OpenBSD: frontend.c,v 1.14 2021/07/26 09:26:36 florian Exp $ */
/*
* Copyright (c) 2017, 2021 Florian Obser <florian@openbsd.org>
struct iface *get_iface_by_id(uint32_t);
void remove_iface(uint32_t);
void set_bpfsock(int, uint32_t);
-ssize_t build_packet(uint8_t, uint32_t, struct ether_addr *, struct
- in_addr *, struct in_addr *);
+ssize_t build_packet(uint8_t, char *, uint32_t, struct ether_addr *,
+ struct in_addr *, struct in_addr *);
void send_discover(struct iface *);
void send_request(struct iface *);
void bpf_send_packet(struct iface *, uint8_t *, ssize_t);
void udp_send_packet(struct iface *, uint8_t *, ssize_t);
LIST_HEAD(, iface) interfaces;
+struct dhcpleased_conf *frontend_conf;
static struct imsgev *iev_main;
static struct imsgev *iev_engine;
struct event ev_route;
struct event ev_sigint, ev_sigterm;
struct passwd *pw;
+#ifndef SMALL
+ frontend_conf = config_new_empty();
+#endif /* SMALL */
+
log_init(debug, LOG_DAEMON);
log_setverbose(verbose);
msgbuf_clear(&iev_main->ibuf.w);
close(iev_main->ibuf.fd);
+#ifndef SMALL
+ config_clear(frontend_conf);
+#endif /* SMALL */
+
free(iev_engine);
free(iev_main);
void
frontend_dispatch_main(int fd, short event, void *bula)
{
- struct imsg imsg;
- struct imsgev *iev = bula;
- struct imsgbuf *ibuf = &iev->ibuf;
- struct iface *iface;
- ssize_t n;
- int shut = 0, bpfsock, if_index, udpsock;
+ static struct dhcpleased_conf *nconf;
+ static struct iface_conf *iface_conf;
+ struct imsg imsg;
+ struct imsgev *iev = bula;
+ struct imsgbuf *ibuf = &iev->ibuf;
+ struct iface *iface;
+ ssize_t n;
+ int shut = 0, bpfsock, if_index, udpsock;
if (event & EV_READ) {
if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
case IMSG_STARTUP:
frontend_startup();
break;
-#ifndef SMALL
+#ifndef SMALL
+ case IMSG_RECONF_CONF:
+ if (nconf != NULL)
+ fatalx("%s: IMSG_RECONF_CONF already in "
+ "progress", __func__);
+ if ((nconf = malloc(sizeof(struct dhcpleased_conf))) ==
+ NULL)
+ fatal(NULL);
+ SIMPLEQ_INIT(&nconf->iface_list);
+ break;
+ case IMSG_RECONF_IFACE:
+ if (IMSG_DATA_SIZE(imsg) != sizeof(struct
+ iface_conf))
+ fatalx("%s: IMSG_RECONF_IFACE wrong length: "
+ "%lu", __func__, IMSG_DATA_SIZE(imsg));
+ if ((iface_conf = malloc(sizeof(struct iface_conf)))
+ == NULL)
+ fatal(NULL);
+ memcpy(iface_conf, imsg.data, sizeof(struct
+ iface_conf));
+ iface_conf->vc_id = NULL;
+ iface_conf->vc_id_len = 0;
+ iface_conf->c_id = NULL;
+ iface_conf->c_id_len = 0;
+ SIMPLEQ_INSERT_TAIL(&nconf->iface_list,
+ iface_conf, entry);
+ break;
+ case IMSG_RECONF_VC_ID:
+ if (iface_conf == NULL)
+ fatal("IMSG_RECONF_VC_ID without "
+ "IMSG_RECONF_IFACE");
+ if (IMSG_DATA_SIZE(imsg) > 255 + 2)
+ fatalx("%s: IMSG_RECONF_VC_ID wrong length: "
+ "%lu", __func__, IMSG_DATA_SIZE(imsg));
+ if ((iface_conf->vc_id = malloc(IMSG_DATA_SIZE(imsg)))
+ == NULL)
+ fatal(NULL);
+ memcpy(iface_conf->vc_id, imsg.data,
+ IMSG_DATA_SIZE(imsg));
+ iface_conf->vc_id_len = IMSG_DATA_SIZE(imsg);
+ break;
+ case IMSG_RECONF_C_ID:
+ if (iface_conf == NULL)
+ fatal("IMSG_RECONF_C_ID without "
+ "IMSG_RECONF_IFACE");
+ if (IMSG_DATA_SIZE(imsg) > 255 + 2)
+ fatalx("%s: IMSG_RECONF_C_ID wrong length: "
+ "%lu", __func__, IMSG_DATA_SIZE(imsg));
+ if ((iface_conf->c_id = malloc(IMSG_DATA_SIZE(imsg)))
+ == NULL)
+ fatal(NULL);
+ memcpy(iface_conf->c_id, imsg.data,
+ IMSG_DATA_SIZE(imsg));
+ iface_conf->c_id_len = IMSG_DATA_SIZE(imsg);
+ break;
+ case IMSG_RECONF_END:
+ if (nconf == NULL)
+ fatalx("%s: IMSG_RECONF_END without "
+ "IMSG_RECONF_CONF", __func__);
+ merge_config(frontend_conf, nconf);
+ nconf = NULL;
+ break;
case IMSG_CONTROLFD:
if ((fd = imsg.fd) == -1)
fatalx("%s: expected to receive imsg "
}
ssize_t
-build_packet(uint8_t message_type, uint32_t xid, struct ether_addr *hw_address,
- struct in_addr *requested_ip, struct in_addr *server_identifier)
+build_packet(uint8_t message_type, char *if_name, uint32_t xid,
+ struct ether_addr *hw_address, struct in_addr *requested_ip,
+ struct in_addr *server_identifier)
{
static uint8_t dhcp_cookie[] = DHCP_COOKIE;
static uint8_t dhcp_message_type[] = {DHO_DHCP_MESSAGE_TYPE, 1,
4, 0, 0, 0, 0};
static uint8_t dhcp_server_identifier[] = {DHO_DHCP_SERVER_IDENTIFIER,
4, 0, 0, 0, 0};
- struct dhcp_hdr *hdr;
- ssize_t len;
- uint8_t *p;
- char *c;
+#ifndef SMALL
+ struct iface_conf *iface_conf;
+#endif /* SMALL */
+ struct dhcp_hdr *hdr;
+ ssize_t len;
+ uint8_t *p;
+ char *c;
+
+#ifndef SMALL
+ iface_conf = find_iface_conf(&frontend_conf->iface_list, if_name);
+#endif /* SMALL */
memset(dhcp_packet, 0, sizeof(dhcp_packet));
dhcp_message_type[2] = message_type;
memcpy(p, dhcp_hostname, dhcp_hostname[1] + 2);
p += dhcp_hostname[1] + 2;
}
- memcpy(dhcp_client_id + 3, hw_address, sizeof(*hw_address));
- memcpy(p, dhcp_client_id, sizeof(dhcp_client_id));
- p += sizeof(dhcp_client_id);
+
+#ifndef SMALL
+ if (iface_conf != NULL) {
+ if (iface_conf->c_id_len > 0) {
+ /* XXX check space */
+ memcpy(p, iface_conf->c_id, iface_conf->c_id_len);
+ p += iface_conf->c_id_len;
+ }
+ if (iface_conf->vc_id_len > 0) {
+ /* XXX check space */
+ memcpy(p, iface_conf->vc_id, iface_conf->vc_id_len);
+ p += iface_conf->vc_id_len;
+ }
+ } else
+#endif /* SMALL */
+ {
+ memcpy(dhcp_client_id + 3, hw_address, sizeof(*hw_address));
+ memcpy(p, dhcp_client_id, sizeof(dhcp_client_id));
+ p += sizeof(dhcp_client_id);
+ }
memcpy(p, dhcp_req_list, sizeof(dhcp_req_list));
p += sizeof(dhcp_req_list);
void
send_discover(struct iface *iface)
{
- ssize_t pkt_len;
- char ifnamebuf[IF_NAMESIZE], *if_name;
+ ssize_t pkt_len;
+ char ifnamebuf[IF_NAMESIZE], *if_name;
if (!event_initialized(&iface->bpfev.ev)) {
iface->send_discover = 1;
if_name = if_indextoname(iface->ifinfo.if_index, ifnamebuf);
log_debug("DHCPDISCOVER on %s", if_name == NULL ? "?" : if_name);
- pkt_len = build_packet(DHCPDISCOVER, iface->xid,
+ pkt_len = build_packet(DHCPDISCOVER, if_name, iface->xid,
&iface->ifinfo.hw_address, &iface->requested_ip, NULL);
bpf_send_packet(iface, dhcp_packet, pkt_len);
}
void
send_request(struct iface *iface)
{
- ssize_t pkt_len;
- char ifnamebuf[IF_NAMESIZE], *if_name;
+ ssize_t pkt_len;
+ char ifnamebuf[IF_NAMESIZE], *if_name;
if_name = if_indextoname(iface->ifinfo.if_index, ifnamebuf);
log_debug("DHCPREQUEST on %s", if_name == NULL ? "?" : if_name);
- pkt_len = build_packet(DHCPREQUEST, iface->xid,
+ pkt_len = build_packet(DHCPREQUEST, if_name, iface->xid,
&iface->ifinfo.hw_address, &iface->requested_ip,
&iface->server_identifier);
if (iface->dhcp_server.s_addr != INADDR_ANY)
send_discover(iface);
}
}
+
+#ifndef SMALL
+struct iface_conf*
+find_iface_conf(struct iface_conf_head *head, char *if_name)
+{
+ struct iface_conf *iface_conf;
+
+ if (if_name == NULL)
+ return (NULL);
+
+ SIMPLEQ_FOREACH(iface_conf, head, entry) {
+ if (strcmp(iface_conf->name, if_name) == 0)
+ return iface_conf;
+ }
+ return (NULL);
+}
+#endif /* SMALL */
--- /dev/null
+/* $OpenBSD: parse.y,v 1.1 2021/07/26 09:26:36 florian Exp $ */
+
+/*
+ * Copyright (c) 2018 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 <netinet/if_ether.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 "dhcpleased.h"
+#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);
+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 {
+ TAILQ_ENTRY(sym) entry;
+ int used;
+ int persist;
+ char *nam;
+ char *val;
+};
+
+int symset(const char *, const char *, int);
+char *symget(const char *);
+
+static struct dhcpleased_conf *conf;
+static int errors;
+
+static struct iface_conf *iface_conf;
+
+struct iface_conf *conf_get_iface(char *);
+
+typedef struct {
+ union {
+ int64_t number;
+ char *string;
+ } v;
+ int lineno;
+} YYSTYPE;
+
+%}
+
+%token DHCP_IFACE ERROR SEND VENDOR CLASS ID CLIENT
+
+%token <v.string> STRING
+%token <v.number> NUMBER
+%type <v.string> string
+
+%%
+
+grammar : /* empty */
+ | grammar '\n'
+ | grammar varset '\n'
+ | grammar dhcp_iface '\n'
+ | grammar error '\n' { file->errors++; }
+ ;
+
+string : string STRING {
+ if (asprintf(&$$, "%s %s", $1, $2) == -1) {
+ free($1);
+ free($2);
+ yyerror("string: asprintf");
+ YYERROR;
+ }
+ free($1);
+ free($2);
+ }
+ | STRING
+ ;
+
+varset : STRING '=' string {
+ char *s = $1;
+ if (log_getverbose() == 1)
+ printf("%s = \"%s\"\n", $1, $3);
+ while (*s++) {
+ if (isspace((unsigned char)*s)) {
+ yyerror("macro name cannot contain "
+ "whitespace");
+ free($1);
+ free($3);
+ YYERROR;
+ }
+ }
+ if (symset($1, $3, 0) == -1)
+ fatal("cannot store variable");
+ free($1);
+ free($3);
+ }
+ ;
+
+optnl : '\n' optnl /* zero or more newlines */
+ | /*empty*/
+ ;
+
+nl : '\n' optnl /* one or more newlines */
+ ;
+
+dhcp_iface : DHCP_IFACE STRING {
+ iface_conf = conf_get_iface($2);
+ } iface_block {
+ iface_conf = NULL;
+ }
+ ;
+
+iface_block : '{' optnl ifaceopts_l '}'
+ | '{' optnl '}'
+ | /* empty */
+ ;
+
+ifaceopts_l : ifaceopts_l ifaceoptsl nl
+ | ifaceoptsl optnl
+ ;
+
+ifaceoptsl : SEND VENDOR CLASS ID STRING {
+ ssize_t len;
+ char buf[256];
+
+ if (iface_conf->vc_id != NULL) {
+ yyerror("vendor class id already set");
+ YYERROR;
+ }
+
+ len = strnunvis(buf, $5, sizeof(buf));
+ free($5);
+
+ if (len == -1) {
+ yyerror("invalid vendor class id");
+ YYERROR;
+ }
+ if ((size_t)len >= sizeof(buf)) {
+ yyerror("vendor class id too long");
+ YYERROR;
+ }
+
+ iface_conf->vc_id_len = 2 + strlen(buf);
+ iface_conf->vc_id = malloc(iface_conf->vc_id_len);
+ if (iface_conf->vc_id == NULL) {
+ yyerror("malloc");
+ YYERROR;
+ }
+ iface_conf->vc_id[0] = DHO_DHCP_CLASS_IDENTIFIER;
+ iface_conf->vc_id[1] = iface_conf->vc_id_len - 2;
+ memcpy(&iface_conf->vc_id[2], buf,
+ iface_conf->vc_id_len - 2);
+ }
+ | SEND CLIENT ID STRING {
+ size_t i;
+ ssize_t len;
+ int not_hex = 0, val;
+ char buf[256], *hex, *p, excess;
+
+ if (iface_conf->c_id != NULL) {
+ yyerror("client-id already set");
+ YYERROR;
+ }
+
+ /* parse as hex string including the type byte */
+ if ((hex = strdup($4)) == NULL) {
+ free($4);
+ yyerror("malloc");
+ YYERROR;
+ }
+ for (i = 0; (p = strsep(&hex, ":")) != NULL && i <
+ sizeof(buf); ) {
+ if (sscanf(p, "%x%c", &val, &excess) != 1 ||
+ val < 0 || val > 0xff) {
+ not_hex = 1;
+ break;
+ }
+ buf[i++] = (val & 0xff);
+ }
+ if (p != NULL && i == sizeof(buf))
+ not_hex = 1;
+ free(hex);
+
+ if (not_hex) {
+ len = strnunvis(buf, $4, sizeof(buf));
+ free($4);
+
+ if (len == -1) {
+ yyerror("invalid client-id");
+ YYERROR;
+ }
+ if ((size_t)len >= sizeof(buf)) {
+ yyerror("client-id too long");
+ YYERROR;
+ }
+ iface_conf->c_id_len = 3 + strlen(buf);
+ iface_conf->c_id = malloc(iface_conf->c_id_len);
+ if (iface_conf->c_id == NULL) {
+ yyerror("malloc");
+ YYERROR;
+ }
+ iface_conf->c_id[2] = HTYPE_NONE;
+ memcpy(&iface_conf->c_id[3], buf,
+ iface_conf->c_id_len - 3);
+ } else {
+ free($4);
+ iface_conf->c_id_len = 2 + i;
+ iface_conf->c_id = malloc(iface_conf->c_id_len);
+ if (iface_conf->c_id == NULL) {
+ yyerror("malloc");
+ YYERROR;
+ }
+ memcpy(&iface_conf->c_id[2], buf,
+ iface_conf->c_id_len - 2);
+ }
+ iface_conf->c_id[0] = DHO_DHCP_CLIENT_IDENTIFIER;
+ iface_conf->c_id[1] = iface_conf->c_id_len - 2;
+ }
+ ;
+%%
+
+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
+kw_cmp(const void *k, const void *e)
+{
+ return (strcmp(k, ((const struct keywords *)e)->k_name));
+}
+
+int
+lookup(char *s)
+{
+ /* This has to be sorted always. */
+ static const struct keywords keywords[] = {
+ {"class", CLASS},
+ {"client", CLIENT},
+ {"id", ID},
+ {"interface", DHCP_IFACE},
+ {"send", SEND},
+ {"vendor", VENDOR},
+ };
+ 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);
+}
+
+#define START_EXPAND 1
+#define DONE_EXPAND 2
+
+static int expanding;
+
+int
+igetc(void)
+{
+ int c;
+
+ while (1) {
+ if (file->ungetpos > 0)
+ c = file->ungetbuf[--file->ungetpos];
+ else
+ c = getc(file->stream);
+
+ if (c == START_EXPAND)
+ expanding = 1;
+ else if (c == DONE_EXPAND)
+ expanding = 0;
+ else
+ break;
+ }
+ return (c);
+}
+
+int
+lgetc(int quotec)
+{
+ int c, next;
+
+ if (quotec) {
+ if ((c = igetc()) == EOF) {
+ yyerror("reached end of file while parsing "
+ "quoted string");
+ if (file == topfile || popfile() == EOF)
+ return (EOF);
+ return (quotec);
+ }
+ return (c);
+ }
+
+ while ((c = igetc()) == '\\') {
+ next = igetc();
+ if (next != '\n') {
+ c = next;
+ break;
+ }
+ yylval.lineno = file->lineno;
+ file->lineno++;
+ }
+
+ if (c == EOF) {
+ /*
+ * Fake EOL when hit EOF for the first time. This gets line
+ * count right if last line in included file is syntactically
+ * invalid and has no newline.
+ */
+ if (file->eof_reached == 0) {
+ file->eof_reached = 1;
+ return ('\n');
+ }
+ while (c == EOF) {
+ if (file == topfile || popfile() == EOF)
+ return (EOF);
+ c = igetc();
+ }
+ }
+ return (c);
+}
+
+void
+lungetc(int c)
+{
+ if (c == EOF)
+ return;
+
+ if (file->ungetpos >= file->ungetsize) {
+ void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
+ if (p == NULL)
+ err(1, "lungetc");
+ file->ungetbuf = p;
+ file->ungetsize *= 2;
+ }
+ file->ungetbuf[file->ungetpos++] = c;
+}
+
+int
+findeol(void)
+{
+ int c;
+
+ /* Skip to either EOF or the first real EOL. */
+ while (1) {
+ c = lgetc(0);
+ if (c == '\n') {
+ file->lineno++;
+ break;
+ }
+ if (c == EOF)
+ break;
+ }
+ return (ERROR);
+}
+
+int
+yylex(void)
+{
+ unsigned char buf[8096];
+ unsigned char *p, *val;
+ int quotec, next, c;
+ int token;
+
+top:
+ p = buf;
+ while ((c = lgetc(0)) == ' ' || c == '\t')
+ ; /* nothing */
+
+ yylval.lineno = file->lineno;
+ if (c == '#')
+ while ((c = lgetc(0)) != '\n' && c != EOF)
+ ; /* nothing */
+ if (c == '$' && !expanding) {
+ while (1) {
+ if ((c = lgetc(0)) == EOF)
+ return (0);
+
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ if (isalnum(c) || c == '_') {
+ *p++ = c;
+ continue;
+ }
+ *p = '\0';
+ lungetc(c);
+ break;
+ }
+ val = symget(buf);
+ if (val == NULL) {
+ yyerror("macro '%s' not defined", buf);
+ return (findeol());
+ }
+ p = val + strlen(val) - 1;
+ lungetc(DONE_EXPAND);
+ while (p >= val) {
+ lungetc(*p);
+ p--;
+ }
+ lungetc(START_EXPAND);
+ goto top;
+ }
+
+ 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(*--p);
+ c = *--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 = lookup(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);
+}
+
+int
+check_file_secrecy(int fd, const char *fname)
+{
+ struct stat st;
+
+ if (fstat(fd, &st)) {
+ log_warn("cannot stat %s", fname);
+ return (-1);
+ }
+ if (st.st_uid != 0 && st.st_uid != getuid()) {
+ log_warnx("%s: owner not root or current user", fname);
+ return (-1);
+ }
+ if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
+ log_warnx("%s: group writable or world read/writable", fname);
+ return (-1);
+ }
+ return (0);
+}
+
+struct file *
+pushfile(const char *name, int secret)
+{
+ struct file *nfile;
+
+ if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
+ log_warn("calloc");
+ return (NULL);
+ }
+ if ((nfile->name = strdup(name)) == NULL) {
+ log_warn("strdup");
+ free(nfile);
+ return (NULL);
+ }
+ if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
+ free(nfile->name);
+ free(nfile);
+ return (NULL);
+ } else if (secret &&
+ check_file_secrecy(fileno(nfile->stream), nfile->name)) {
+ fclose(nfile->stream);
+ free(nfile->name);
+ free(nfile);
+ return (NULL);
+ }
+ nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
+ nfile->ungetsize = 16;
+ nfile->ungetbuf = malloc(nfile->ungetsize);
+ if (nfile->ungetbuf == NULL) {
+ log_warn("malloc");
+ fclose(nfile->stream);
+ free(nfile->name);
+ free(nfile);
+ return (NULL);
+ }
+ TAILQ_INSERT_TAIL(&files, nfile, entry);
+ return (nfile);
+}
+
+int
+popfile(void)
+{
+ struct file *prev;
+
+ if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
+ prev->errors += file->errors;
+
+ TAILQ_REMOVE(&files, file, entry);
+ fclose(file->stream);
+ free(file->name);
+ free(file->ungetbuf);
+ free(file);
+ file = prev;
+ return (file ? 0 : EOF);
+}
+
+struct dhcpleased_conf *
+parse_config(char *filename)
+{
+ struct sym *sym, *next;
+
+ conf = config_new_empty();
+
+ file = pushfile(filename != NULL ? filename : _PATH_CONF_FILE, 0);
+ if (file == NULL) {
+ /* no default config file is fine */
+ if (errno == ENOENT && filename == NULL)
+ return (conf);
+ log_warn("%s", filename);
+ free(conf);
+ return (NULL);
+ }
+ topfile = file;
+
+ yyparse();
+ errors = file->errors;
+ popfile();
+
+ /* Free macros and check which have not been used. */
+ TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
+ if ((log_getverbose() == 2) && !sym->used)
+ fprintf(stderr, "warning: macro '%s' not used\n",
+ sym->nam);
+ if (!sym->persist) {
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entry);
+ free(sym);
+ }
+ }
+
+ if (errors) {
+ config_clear(conf);
+ return (NULL);
+ }
+
+ return (conf);
+}
+
+int
+symset(const char *nam, const char *val, int persist)
+{
+ struct sym *sym;
+
+ TAILQ_FOREACH(sym, &symhead, entry) {
+ if (strcmp(nam, sym->nam) == 0)
+ break;
+ }
+
+ if (sym != NULL) {
+ if (sym->persist == 1)
+ return (0);
+ else {
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entry);
+ free(sym);
+ }
+ }
+ if ((sym = calloc(1, sizeof(*sym))) == NULL)
+ return (-1);
+
+ sym->nam = strdup(nam);
+ if (sym->nam == NULL) {
+ free(sym);
+ return (-1);
+ }
+ sym->val = strdup(val);
+ if (sym->val == NULL) {
+ free(sym->nam);
+ free(sym);
+ return (-1);
+ }
+ sym->used = 0;
+ sym->persist = persist;
+ TAILQ_INSERT_TAIL(&symhead, sym, entry);
+ 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)
+{
+ struct sym *sym;
+
+ TAILQ_FOREACH(sym, &symhead, entry) {
+ if (strcmp(nam, sym->nam) == 0) {
+ sym->used = 1;
+ return (sym->val);
+ }
+ }
+ return (NULL);
+}
+
+struct iface_conf *
+conf_get_iface(char *name)
+{
+ struct iface_conf *iface;
+ size_t n;
+
+ SIMPLEQ_FOREACH(iface, &conf->iface_list, entry) {
+ if (strcmp(name, iface->name) == 0)
+ return (iface);
+ }
+
+ iface = calloc(1, sizeof(*iface));
+ if (iface == NULL)
+ errx(1, "%s: calloc", __func__);
+ n = strlcpy(iface->name, name, sizeof(iface->name));
+ if (n >= sizeof(iface->name))
+ errx(1, "%s: name too long", __func__);
+
+
+ SIMPLEQ_INSERT_TAIL(&conf->iface_list, iface, entry);
+
+ return (iface);
+}
--- /dev/null
+/* $OpenBSD: printconf.c,v 1.1 2021/07/26 09:26:36 florian Exp $ */
+
+/*
+ * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
+ * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
+ *
+ * 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/uio.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <arpa/inet.h>
+
+#include <event.h>
+#include <imsg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <vis.h>
+
+#include "dhcpleased.h"
+#include "log.h"
+
+void print_dhcp_options(char *, uint8_t *, int);
+
+void
+print_dhcp_options(char *indent, uint8_t *p, int len)
+{
+ static char buf[4 * 1500 + 1];
+ int rem, i;
+ uint8_t dho, dho_len, type;
+
+ rem = len;
+
+ while (rem > 0) {
+ dho = *p;
+ p += 1;
+ rem -= 1;
+
+ if (rem == 0)
+ fatal("dhcp option too short");
+ dho_len = *p;
+ p += 1;
+ rem -= 1;
+ if (rem < dho_len)
+ fatal("dhcp option too short: %d %d", rem, dho_len);
+
+ switch (dho) {
+ case DHO_DHCP_CLASS_IDENTIFIER:
+ strvisx(buf, p, dho_len, VIS_DQ | VIS_CSTYLE);
+ p += dho_len;
+ rem -= dho_len;
+ printf("%ssend vendor class id \"%s\"\n", indent, buf);
+ break;
+ case DHO_DHCP_CLIENT_IDENTIFIER:
+ if (dho_len < 1)
+ fatal("dhcp option too short");
+ type = *p;
+ p += 1;
+ rem -= 1;
+ switch (type) {
+ case HTYPE_NONE:
+ strvisx(buf, p, dho_len - 1, VIS_DQ |
+ VIS_CSTYLE);
+ printf("%ssend client id \"%s\"\n",
+ indent, buf);
+ break;
+ default:
+ printf("%ssend client id \"%02x",
+ indent, type);
+
+ for (i = 0; i < dho_len - 1; i++) {
+ printf(":%02x", *(p + i));
+ }
+ printf("\"\n");
+ break;
+ }
+ p += dho_len - 1;
+ rem -= dho_len - 1;
+ break;
+ default:
+ fatal("unknown dhcp option: %d [%d]", *p, rem);
+ break;
+ }
+ }
+}
+
+void
+print_config(struct dhcpleased_conf *conf)
+{
+ struct iface_conf *iface;
+
+ SIMPLEQ_FOREACH(iface, &conf->iface_list, entry) {
+ printf("interface %s {\n", iface->name);
+ print_dhcp_options("\t", iface->c_id, iface->c_id_len);
+ print_dhcp_options("\t", iface->vc_id, iface->vc_id_len);
+ printf("}\n");
+ }
+}
-/* $OpenBSD: dhcpleasectl.c,v 1.4 2021/06/16 14:06:18 florian Exp $ */
+/* $OpenBSD: dhcpleasectl.c,v 1.5 2021/07/26 09:26:36 florian Exp $ */
/*
* Copyright (c) 2021 Florian Obser <florian@openbsd.org>
/* Process user request. */
switch (res->action) {
+ case RELOAD:
+ imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0);
+ printf("reload request sent.\n");
+ done = 1;
+ break;
case LOG_VERBOSE:
verbose = 1;
/* FALLTHROUGH */
-/* $OpenBSD: parser.c,v 1.1 2021/02/26 16:16:37 florian Exp $ */
+/* $OpenBSD: parser.c,v 1.2 2021/07/26 09:26:36 florian Exp $ */
/*
* Copyright (c) 2004 Esben Norby <norby@openbsd.org>
static const struct token t_send_request[];
static const struct token t_main[] = {
+ {KEYWORD, "reload", RELOAD, NULL},
{KEYWORD, "show", SHOW, t_show},
{KEYWORD, "log", NONE, t_log},
{KEYWORD, "send", NONE, t_send},
-/* $OpenBSD: parser.h,v 1.1 2021/02/26 16:16:37 florian Exp $ */
+/* $OpenBSD: parser.h,v 1.2 2021/07/26 09:26:36 florian Exp $ */
/*
* Copyright (c) 2004 Esben Norby <norby@openbsd.org>
LOG_BRIEF,
SHOW,
SHOW_INTERFACE,
- SEND_REQUEST
+ SEND_REQUEST,
+ RELOAD
};
struct parse_result {