[patch] Remove unnecessary timerclear in tcpbench

2018-05-11 Thread Nan Xiao
Hi tech@,

Following is a trivial modification which removes unnecessary
initialization of timeval. I am Sorry if this patch seems a little picky.

Index: tcpbench.c
===
RCS file: /cvs/src/usr.bin/tcpbench/tcpbench.c,v
retrieving revision 1.55
diff -u -p -r1.55 tcpbench.c
--- tcpbench.c  10 May 2018 14:29:17 -  1.55
+++ tcpbench.c  12 May 2018 02:06:54 -
@@ -253,7 +253,6 @@ set_slice_timer(int on)
if (on) {
if (evtimer_pending(, NULL))
return;
-   timerclear();
/* XXX Is there a better way to do this ? */
tv.tv_sec = ptb->rflag / 1000;
tv.tv_usec = (ptb->rflag % 1000) * 1000;

-- 
Best Regards
Nan Xiao



kevent, nanosleep: tighten valid range for incoming tv_nsec

2018-05-11 Thread Scott Cheloha
Hi,

This fixes timeout input validation for kevent(2) and nanosleep(2).

nanosleep() and kqueue_scan() (callee of kevent()) both convert the
incoming timespec to a timeval with TIMESPEC_TO_TIMEVAL *before*
validating the timeout.  This hides invalid tv_nsec values on
[-999, 0) and (10, 100999].

The patch moves the range check up to before the conversion in
kqueue_scan() and drops the conversion from nanosleep() entirely;
we use the copied timespec directly instead.  It also adds new
regress tests for both interfaces.

To avoid changes in behavior for both interfaces we need to check
by hand that tv_sec <= 100 million, as timespecfix() quietly truncates
tv_sec to 100 million where itimerfix() would have returned an error.

itimerfix() also quietly rounds non-zero tv_usec up to a single tick
where timespecfix() does not.  Thus we need to explicitly round the
converted timeval in kqueue_scan().  We don't need to round the timespec
in nanosleep(), though, because tstohz() is wrapped in MAX() which
ensures we tsleep at least one tick, just like before.

FreeBSD, NetBSD, illumos, and Linux, and all correctly (per the POSIX spec)
validate the input timespec for nanosleep, so I do not believe this change
will break anything in ports.

FreeBSD and NetBSD both check the incoming timespec in kqueue_scan()
against the tighter bound on tv_nsec, so I think it makes sense to do so
as well.

ok?

--
Scott Cheloha

Index: sys/kern/kern_event.c
===
RCS file: /cvs/src/sys/kern/kern_event.c,v
retrieving revision 1.88
diff -u -p -r1.88 kern_event.c
--- sys/kern/kern_event.c   27 Apr 2018 10:13:37 -  1.88
+++ sys/kern/kern_event.c   11 May 2018 22:49:37 -
@@ -693,6 +693,7 @@ kqueue_scan(struct kqueue *kq, int maxev
const struct timespec *tsp, struct proc *p, int *retval)
 {
struct kevent *kevp;
+   struct timespec ats;
struct timeval atv, rtv, ttv;
struct knote *kn, marker;
int s, count, timeout, nkev = 0, error = 0;
@@ -703,16 +704,18 @@ kqueue_scan(struct kqueue *kq, int maxev
goto done;
 
if (tsp != NULL) {
-   TIMESPEC_TO_TIMEVAL(, tsp);
-   if (tsp->tv_sec == 0 && tsp->tv_nsec == 0) {
+   ats = *tsp;
+   if (ats.tv_sec > 1 || timespecfix()) {
+   error = EINVAL;
+   goto done;
+   }
+   TIMESPEC_TO_TIMEVAL(, );
+   if (atv.tv_sec == 0 && atv.tv_usec == 0) {
/* No timeout, just poll */
timeout = -1;
goto start;
}
-   if (itimerfix()) {
-   error = EINVAL;
-   goto done;
-   }
+   itimerround();
 
timeout = atv.tv_sec > 24 * 60 * 60 ?
24 * 60 * 60 * hz : tvtohz();
Index: sys/kern/kern_time.c
===
RCS file: /cvs/src/sys/kern/kern_time.c,v
retrieving revision 1.101
diff -u -p -r1.101 kern_time.c
--- sys/kern/kern_time.c19 Feb 2018 08:59:52 -  1.101
+++ sys/kern/kern_time.c11 May 2018 22:49:37 -
@@ -268,7 +268,6 @@ sys_nanosleep(struct proc *p, void *v, r
struct timespec rqt, rmt;
struct timespec sts, ets;
struct timespec *rmtp;
-   struct timeval tv;
int error, error1;
 
rmtp = SCARG(uap, rmtp);
@@ -283,15 +282,14 @@ sys_nanosleep(struct proc *p, void *v, r
}
 #endif
 
-   TIMESPEC_TO_TIMEVAL(, );
-   if (itimerfix())
+   if (rqt.tv_sec > 1 || timespecfix())
return (EINVAL);
 
if (rmtp)
getnanouptime();
 
error = tsleep(, PWAIT | PCATCH, "nanosleep",
-   MAX(1, tvtohz()));
+   MAX(1, tstohz()));
if (error == ERESTART)
error = EINTR;
if (error == EWOULDBLOCK)
Index: regress/sys/kern/nanosleep/Makefile
===
RCS file: /cvs/src/regress/sys/kern/nanosleep/Makefile,v
retrieving revision 1.3
diff -u -p -r1.3 Makefile
--- regress/sys/kern/nanosleep/Makefile 2 Sep 2002 20:01:44 -   1.3
+++ regress/sys/kern/nanosleep/Makefile 11 May 2018 22:49:37 -
@@ -18,7 +18,10 @@ time_elapsed_with_signal: nanosleep
 short_time: nanosleep
./nanosleep -S
 
+invalid_time: nanosleep
+   ./nanosleep -i
+
 REGRESS_TARGETS=trivial with_signal time_elapsed time_elapsed_with_signal
-REGRESS_TARGETS+=short_time
+REGRESS_TARGETS+=short_time invalid_time
 
 .include 
Index: regress/sys/kern/nanosleep/nanosleep.c
===
RCS file: /cvs/src/regress/sys/kern/nanosleep/nanosleep.c,v
retrieving revision 1.6
diff -u -p -r1.6 nanosleep.c
--- regress/sys/kern/nanosleep/nanosleep.c  

ldapd: filter rules on attributes

2018-05-11 Thread Reyk Floeter
Hi!

(resent to tech@)
the following ldapd patch allows filter rules to match on attributes.

This can be used to allow users to change their password (and a few
other things) but not their entire dn.

For example, in ldapd.conf:

allow read access to any by self
allow write access to any attribute shadowPassword by self
allow write access to any attribute sshPublicKey by self
allow write access to any attribute gecos by self
allow write access to any attribute loginShell by self

Alternatively, this would also work:

allow write access to any by self
deny write access to any attribute mail by self

This only supports "write" (modify, add, delete) and not "read"
(search) filter rules.  The search mode will be more complicated and I
will look at this later.

Thoughts?  OK?

Reyk

Index: usr.sbin/ldapd/auth.c
===
RCS file: /cvs/src/usr.sbin/ldapd/auth.c,v
retrieving revision 1.12
diff -u -p -u -p -r1.12 auth.c
--- usr.sbin/ldapd/auth.c   20 Jan 2017 11:55:08 -  1.12
+++ usr.sbin/ldapd/auth.c   11 May 2018 14:09:01 -
@@ -33,7 +33,7 @@
 
 static int
 aci_matches(struct aci *aci, struct conn *conn, struct namespace *ns,
-char *dn, int rights, enum scope scope)
+char *dn, int rights, char *attr, enum scope scope)
 {
struct btval key;
 
@@ -98,6 +98,13 @@ aci_matches(struct aci *aci, struct conn
return 0;
}
 
+   if (aci->attribute != NULL) {
+   if (attr == NULL)
+   return 0;
+   if (strcasecmp(aci->attribute, attr) != 0)
+   return 0;
+   }
+
return 1;
 }
 
@@ -105,7 +112,7 @@ aci_matches(struct aci *aci, struct conn
  */
 int
 authorized(struct conn *conn, struct namespace *ns, int rights, char *dn,
-int scope)
+char *attr, int scope)
 {
struct aci  *aci;
int  type = ACI_ALLOW;
@@ -124,33 +131,41 @@ authorized(struct conn *conn, struct nam
if ((rights & (ACI_WRITE | ACI_CREATE)) != 0)
type = ACI_DENY;
 
-   log_debug("requesting %02X access to %s by %s, in namespace %s",
+   log_debug("requesting %02X access to %s%s%s by %s, in namespace %s",
rights,
dn ? dn : "any",
+   attr ? " attribute " : "",
+   attr ? attr : "",
conn->binddn ? conn->binddn : "any",
ns ? ns->suffix : "global");
 
SIMPLEQ_FOREACH(aci, >acl, entry) {
-   if (aci_matches(aci, conn, ns, dn, rights, scope)) {
+   if (aci_matches(aci, conn, ns, dn, rights,
+   attr, scope)) {
type = aci->type;
-   log_debug("%s by: %s %02X access to %s by %s",
+   log_debug("%s by: %s %02X access to %s%s%s by %s",
type == ACI_ALLOW ? "allowed" : "denied",
aci->type == ACI_ALLOW ? "allow" : "deny",
aci->rights,
aci->target ? aci->target : "any",
+   aci->attribute ? " attribute " : "",
+   aci->attribute ? aci->attribute : "",
aci->subject ? aci->subject : "any");
}
}
 
if (ns != NULL) {
SIMPLEQ_FOREACH(aci, >acl, entry) {
-   if (aci_matches(aci, conn, ns, dn, rights, scope)) {
+   if (aci_matches(aci, conn, ns, dn, rights,
+   attr, scope)) {
type = aci->type;
-   log_debug("%s by: %s %02X access to %s by %s",
+   log_debug("%s by: %s %02X access to %s%s%s by 
%s",
type == ACI_ALLOW ? "allowed" : "denied",
aci->type == ACI_ALLOW ? "allow" : "deny",
aci->rights,
aci->target ? aci->target : "any",
+   aci->attribute ? " attribute " : "",
+   aci->attribute ? aci->attribute : "",
aci->subject ? aci->subject : "any");
}
}
@@ -319,7 +334,7 @@ ldap_auth_simple(struct request *req, ch
return LDAP_INVALID_CREDENTIALS;
} else {
if (!authorized(req->conn, ns, ACI_BIND, binddn,
-   LDAP_SCOPE_BASE))
+   NULL, LDAP_SCOPE_BASE))
return LDAP_INSUFFICIENT_ACCESS;
 
elm = namespace_get(ns, binddn);
Index: usr.sbin/ldapd/ldapd.conf.5
===
RCS file: /cvs/src/usr.sbin/ldapd/ldapd.conf.5,v
retrieving revision 1.22
diff -u -p -u -p 

Re: [PATCH] [src] usr.sbin/smtpd/smtp.1 - normalise protocol definitions

2018-05-11 Thread Jason McIntyre
On Fri, May 11, 2018 at 12:05:34PM +0100, Raf Czlonka wrote:
> Hi all,
> 
> This patch normalises the protocol definitions.
> 
> Regards,
> 
> Raf
> 

i'm ok with this. any objections?
jmc

> Index: usr.sbin/smtpd/smtp.1
> ===
> RCS file: /cvs/src/usr.sbin/smtpd/smtp.1,v
> retrieving revision 1.5
> diff -u -p -r1.5 smtp.1
> --- usr.sbin/smtpd/smtp.1 29 Apr 2018 11:58:45 -  1.5
> +++ usr.sbin/smtpd/smtp.1 11 May 2018 10:59:58 -
> @@ -66,7 +66,7 @@ The following protocols are available:
>  .Pp
>  .Bl -tag -width "smtp+notls" -compact
>  .It smtp
> -Normal SMTP session, with opportunistic STARTTLS.
> +Normal SMTP session with opportunistic STARTTLS.
>  .It smtp+tls
>  Normal SMTP session with mandatory STARTTLS.
>  .It smtp+notls
> @@ -76,7 +76,7 @@ LMTP session with opportunistic STARTTLS
>  .It lmtp+tls
>  LMTP session with mandatory STARTTLS.
>  .It lmtp+notls
> -Plain text LMTP session.
> +Plain text LMTP session without TLS.
>  .It smtps
>  SMTP session with forced TLS on connection.
>  .El
> 



Re: relayctl friendlier

2018-05-11 Thread Reyk Floeter
eOn Fri, May 11, 2018 at 01:53:58PM +0300, Kapetanakis Giannis wrote:
> Hi,
> 
> By default we have:
> 
> # relayctl show
> missing argument:
> valid commands/args:
>   summary
>   hosts
>   redirects
>   relays
>   routers
>   sessions
> 
> On the other hand:
> # relayctl host
> usage: relayctl [-s socket] command [argument ...]
> 
> # relayctl host dis
> missing argument:
> valid commands/args:
>   
> 
> I think it's better if it is like:
> 
> # ./relayctl host
> missing argument:
> valid commands/args:
>   disable
>   enable
> 
> same for table, redirect
> 

Thanks, the diff looks fine

> If this is accepted maybe NOTOKEN can be completely removed from code.
> 

It is just a value and it is better to keep it - the parser code is
somewhat generic and used by many tools in OpenBSD.

Reyk

> regards,
> 
> G
> 
> Index: parser.c
> ===
> RCS file: /cvs/src/usr.sbin/relayctl/parser.c,v
> retrieving revision 1.27
> diff -u -p -r1.27 parser.c
> --- parser.c22 Jan 2015 17:42:09 -  1.27
> +++ parser.c11 May 2018 10:52:11 -
> @@ -81,21 +81,18 @@ static const struct token t_show[] = {
>  };
>  
>  static const struct token t_rdr[] = {
> -   {NOTOKEN,   "", NONE,   NULL},
> {KEYWORD,   "disable",  RDR_DISABLE,t_rdr_id},
> {KEYWORD,   "enable",   RDR_ENABLE, t_rdr_id},
> {ENDTOKEN,  "", NONE,   NULL}
>  };
>  
>  static const struct token t_table[] = {
> -   {NOTOKEN,   "", NONE,   NULL},
> {KEYWORD,   "disable",  TABLE_DISABLE,  t_table_id},
> {KEYWORD,   "enable",   TABLE_ENABLE,   t_table_id},
> {ENDTOKEN,  "", NONE,   NULL}
>  };
>  
>  static const struct token t_host[] = {
> -   {NOTOKEN,   "", NONE,   NULL},
> {KEYWORD,   "disable",  HOST_DISABLE,   t_host_id},
> {KEYWORD,   "enable",   HOST_ENABLE,t_host_id},
> {ENDTOKEN,  "", NONE,   NULL}
> 

-- 



[PATCH] [src] usr.sbin/smtpd/smtp.1 - normalise protocol definitions

2018-05-11 Thread Raf Czlonka
Hi all,

This patch normalises the protocol definitions.

Regards,

Raf

Index: usr.sbin/smtpd/smtp.1
===
RCS file: /cvs/src/usr.sbin/smtpd/smtp.1,v
retrieving revision 1.5
diff -u -p -r1.5 smtp.1
--- usr.sbin/smtpd/smtp.1   29 Apr 2018 11:58:45 -  1.5
+++ usr.sbin/smtpd/smtp.1   11 May 2018 10:59:58 -
@@ -66,7 +66,7 @@ The following protocols are available:
 .Pp
 .Bl -tag -width "smtp+notls" -compact
 .It smtp
-Normal SMTP session, with opportunistic STARTTLS.
+Normal SMTP session with opportunistic STARTTLS.
 .It smtp+tls
 Normal SMTP session with mandatory STARTTLS.
 .It smtp+notls
@@ -76,7 +76,7 @@ LMTP session with opportunistic STARTTLS
 .It lmtp+tls
 LMTP session with mandatory STARTTLS.
 .It lmtp+notls
-Plain text LMTP session.
+Plain text LMTP session without TLS.
 .It smtps
 SMTP session with forced TLS on connection.
 .El



relayctl friendlier

2018-05-11 Thread Kapetanakis Giannis
Hi,

By default we have:

# relayctl show
missing argument:
valid commands/args:
  summary
  hosts
  redirects
  relays
  routers
  sessions

On the other hand:
# relayctl host
usage: relayctl [-s socket] command [argument ...]

# relayctl host dis
missing argument:
valid commands/args:
  

I think it's better if it is like:

# ./relayctl host
missing argument:
valid commands/args:
  disable
  enable

same for table, redirect

If this is accepted maybe NOTOKEN can be completely removed from code.

regards,

G

Index: parser.c
===
RCS file: /cvs/src/usr.sbin/relayctl/parser.c,v
retrieving revision 1.27
diff -u -p -r1.27 parser.c
--- parser.c22 Jan 2015 17:42:09 -  1.27
+++ parser.c11 May 2018 10:52:11 -
@@ -81,21 +81,18 @@ static const struct token t_show[] = {
 };
 
 static const struct token t_rdr[] = {
-   {NOTOKEN,   "", NONE,   NULL},
{KEYWORD,   "disable",  RDR_DISABLE,t_rdr_id},
{KEYWORD,   "enable",   RDR_ENABLE, t_rdr_id},
{ENDTOKEN,  "", NONE,   NULL}
 };
 
 static const struct token t_table[] = {
-   {NOTOKEN,   "", NONE,   NULL},
{KEYWORD,   "disable",  TABLE_DISABLE,  t_table_id},
{KEYWORD,   "enable",   TABLE_ENABLE,   t_table_id},
{ENDTOKEN,  "", NONE,   NULL}
 };
 
 static const struct token t_host[] = {
-   {NOTOKEN,   "", NONE,   NULL},
{KEYWORD,   "disable",  HOST_DISABLE,   t_host_id},
{KEYWORD,   "enable",   HOST_ENABLE,t_host_id},
{ENDTOKEN,  "", NONE,   NULL}



Re: Say no to LARVAL

2018-05-11 Thread Mathieu -
Hello,

So I was actually curious about that, obviously posix is silent on the
subject.

Linux actually returns EBUSY at the dup2 call, which kinda makes
sense. Dfly/Netbsd sleep in the dup call to wait for the file
to be fully constructed.

Linux even has a note in the code where the mention obsd about the
behavior :
https://github.com/torvalds/linux/blob/master/fs/file.c#L810


My 2 cts,
Mathieu-


Philip Guenther wrote:
> On Wed, May 9, 2018 at 3:31 AM, Martin Pieuchot  wrote:
> 
> > On 08/05/18(Tue) 14:12, Philip Guenther wrote:
> > > On Tue, 8 May 2018, Martin Pieuchot wrote:
> > > > The way our kernel allocates and populates new 'struct file *' is
> > > > currently a complete mess.  One of the problems is that it puts
> > > > incomplete descriptors on the global `filehead' list and on the
> > > > open file array `fd_ofiles'.  But to make sure that other threads
> > > > won't use such descriptors before they are ready, they are first
> > > > marked as LARVAL then unmarked via the FILE_SET_MATURE() macro.
> > >
> > > The reason we do that is to meet two goals:
> > > 1) we want to allocate the fd (and fail if we can't) before
> > >possibly blocking while completing the creation of the struct file.
> > >For example, in doaccept(), this thread being blocked waiting for a
> > new
> > >connection must not block other threads from making fd changes.
> >
> > But the way I understand it, allocating the fd isn't changed by my diff.
> > fdalloc(), called by falloc(), still allocates it and mark the
> > corresponding
> > bits with fd_used().
> >
> 
> Those bits don't block dup2() *targeting* the fd, or close of the fd.
> 
> 
> 
> > > 2) close(half_created) and dup2(old, half_created) must be safe: it must
> > >not leak the half_created file in the kernel, and on completion the
> > >half_created fd must either be closed or dup'ed to; the half-created
> > >file must not suddenly show up there later
> >
> > I don't understand, are you talking about close(2) and dup2(2)?  How
> > syscalls are related to half_created files?  How is that related to
> > my explanation below?
> >
> 
> thread 1: socket() -- > returns 3
> thread 1: bind(3, someaddr); listen(3);
> thread 1: accept(3) --> expected to return 4 (it's the first free fd), but
> blocks waiting for connection
> thread 2:   socket() --> returns 5 (if not, then explain that and
> ignore the rest of this sequences)
> thread 2:   dup2(0, 4) --> better not block!  after this fd 4 must be a
> dup of 0!
> thread 2:   connect(5, someaddr) --> unblocks thread 1
> 
> So what happens at the end of this?  Did accept() return 4 as expected and
> if so is fd 4 a duplicate of fd 0 or is it the accepted socket?  I argue
> that it must be a dup of 0 because dup2 succeeded, and the accept that
> completed later cannot close that fd.  That's how the current kernel
> behaves.
> 
> I haven't tested it, but my reading of the diff is that is fd 4 will be the
> accepted fd and the dup'ed copy of fd 0 will be lost, leaving it's
> reference count too high by one.
> 
> 
> Philip



Re: [patch] adds include statement in dhcpd.conf

2018-05-11 Thread Denis Fondras
Hi Julien,

I like the idea but implementation seems a bit naive.
Do you have a working example ?

While testing, I can notice a bug with line count (my latest hobby :D).

$ dhcpd -dn -c /etc/examples/dhcpd.conf
/etc/examples/dhcpd.conf line 21: 
filenameee 
^
/etc/examples/dhcpd.conf line 24: /etc/examples/dhcpd.conf line 21: 

^
fatal in dhcpd: Configuration file errors encountered

My test file :

* /etc/examples/dhcpd.conf :
option  domain-name "my.domain";
option  domain-name-servers 192.168.1.3, 192.168.1.5;

subnet 192.168.1.0 netmask 255.255.255.0 {
option routers 192.168.1.1;

range 192.168.1.32 192.168.1.127;

host static-client {
hardware ethernet 22:33:44:55:66:77;
fixed-address 192.168.1.200;
}

host pxe-client {
hardware ethernet 02:03:04:05:06:07;
filename "pxeboot";
next-server 192.168.1.1;
}
include "/usr/src/usr.sbin/dhcpd/host.conf";
}

#include "/usr/src/usr.sbin/dhcpd/subnet.conf";

* /usr/src/usr.sbin/dhcpd/host.conf :
host pxe-client2 {
hardware ethernet 01:03:04:05:06:07;
filenameee "pxeboot";
next-server 192.168.1.10;
}



dhcpd.conf isn't 24 lines long and the problem is in the included file
host.conf.

Thanks.
Denis


On Fri, May 11, 2018 at 02:00:11AM +0200, Julien Dhaille wrote:
> Hi,
> this diff implements the “include” statement, like other daemons.
> Also the config file can be split between different files (in my case, a big 
> list of client is generated from a script, and I don’t want to modify 
> dhcpd.conf).
> 
> Although, I am not even sure if this diff is decent and if it’s a good idea.
> Have a good day :)
> 
> 
> Index: conflex.c
> ===
> RCS file: /cvs/src/usr.sbin/dhcpd/conflex.c,v
> retrieving revision 1.19
> diff -u -p -u -p -r1.19 conflex.c
> --- conflex.c 24 Apr 2017 14:58:36 -  1.19
> +++ conflex.c 10 May 2018 23:30:56 -
> @@ -321,6 +321,7 @@ static const struct keywords {
>   { "hardware",   TOK_HARDWARE },
>   { "host",   TOK_HOST },
>   { "hostname",   TOK_HOSTNAME },
> + { "include",TOK_INCLUDE },
>   { "ipsec-tunnel",   TOK_IPSEC_TUNNEL },
>   { "lease",  TOK_LEASE },
>   { "max-lease-time", TOK_MAX_LEASE_TIME },
> Index: confpars.c
> ===
> RCS file: /cvs/src/usr.sbin/dhcpd/confpars.c,v
> retrieving revision 1.33
> diff -u -p -u -p -r1.33 confpars.c
> --- confpars.c24 Apr 2017 14:58:36 -  1.33
> +++ confpars.c10 May 2018 23:30:56 -
> @@ -329,6 +329,23 @@ parse_statement(FILE *cfile, struct grou
>   parse_warn("use-host-decl-names not allowed here.");
>   group->use_host_decl_names = parse_boolean(cfile);
>   break;
> +
> + case TOK_INCLUDE:
> + group->include = parse_string(cfile);
> + if ((cfile = fopen(group->include, "r")) == NULL)
> + fatal("Can't open %s", group->include);
> + do {
> + token = peek_token(, cfile);
> + if (token == EOF)
> + break;
> + declaration = parse_statement(cfile, _group,
> + ROOT_GROUP,
> + NULL,
> + declaration);
> + } while (1);
> + token = next_token(, cfile); /* Clear the peek buffer */
> + fclose(cfile);
> + break;
> 
>   case TOK_USE_LEASE_ADDR_FOR_DEFAULT_ROUTE:
>   group->use_lease_addr_for_default_route =
> Index: dhcpd.conf.5
> ===
> RCS file: /cvs/src/usr.sbin/dhcpd/dhcpd.conf.5,v
> retrieving revision 1.23
> diff -u -p -u -p -r1.23 dhcpd.conf.5
> --- dhcpd.conf.5  1 Mar 2018 20:48:11 -   1.23
> +++ dhcpd.conf.5  10 May 2018 23:30:56 -
> @@ -873,6 +873,25 @@ into its response (DHCP ACK or NAK) per
>  In other words if the client sends the option it will receive it back.
>  By default, this flag is on
>  and client identifiers will be echoed back to the client.
> +.Pp
> +The
> +.Ic include
> +statement allows additional configuration files to be included:
> +.Pp
> +.D1 Ic include Qq Ar filename ;
> +.Pp
> +For example:
> +.Bd -literal -offset indent
> +include "/etc/dhcpd.conf.hosts";
> +include "/etc/dhcpd.conf.office1";
> +include "/etc/dhcpd.conf.office2";
> +.Ed
> +.Pp
> +You can split the client declarations into different files.
> +It could be use in order to keep
> +.Nm
> +small and easy to read, and if you want to generate clients declaration