Retire MINIX dhcpd(8)
Change-Id: I4b8c738b6176ce390a7a7817b0dcaf9caffe636c
This commit is contained in:
parent
035c234ade
commit
9490732a59
@ -326,7 +326,7 @@
|
||||
./usr/bin/deroff minix-base
|
||||
./usr/bin/devmand minix-base
|
||||
./usr/bin/devsize minix-base
|
||||
./usr/bin/dhcpd minix-base
|
||||
./usr/bin/dhcpd minix-base obsolete
|
||||
./usr/bin/dhrystone minix-base
|
||||
./usr/bin/diff minix-base
|
||||
./usr/bin/dirname minix-base
|
||||
@ -609,7 +609,7 @@
|
||||
./usr/bin/znew minix-base
|
||||
./usr/etc minix-base
|
||||
./usr/etc/daily minix-base
|
||||
./usr/etc/dhcptags.conf minix-base
|
||||
./usr/etc/dhcptags.conf minix-base obsolete
|
||||
./usr/etc/rc minix-base
|
||||
./usr/include/c++ minix-base gcc=5
|
||||
./usr/include/c++/experimental minix-base gcc=5
|
||||
|
@ -254,7 +254,7 @@
|
||||
./usr/libdata/debug/usr/bin/deroff.debug minix-debug debug
|
||||
./usr/libdata/debug/usr/bin/devmand.debug minix-debug debug
|
||||
./usr/libdata/debug/usr/bin/devsize.debug minix-debug debug
|
||||
./usr/libdata/debug/usr/bin/dhcpd.debug minix-debug debug
|
||||
./usr/libdata/debug/usr/bin/dhcpd.debug minix-debug debug,obsolete
|
||||
./usr/libdata/debug/usr/bin/dhrystone.debug minix-debug debug
|
||||
./usr/libdata/debug/usr/bin/diff.debug minix-debug debug
|
||||
./usr/libdata/debug/usr/bin/dirname.debug minix-debug debug
|
||||
|
@ -3274,7 +3274,7 @@
|
||||
./usr/man/man5/configfile.5 minix-man
|
||||
./usr/man/man5/cpio.5 minix-man
|
||||
./usr/man/man5/crontab.5 minix-man
|
||||
./usr/man/man5/dhcp.conf.5 minix-man
|
||||
./usr/man/man5/dhcp.conf.5 minix-man obsolete
|
||||
./usr/man/man5/dir.5 minix-man obsolete
|
||||
./usr/man/man5/editrc.5 minix-man
|
||||
./usr/man/man5/ethers.5 minix-man
|
||||
@ -3382,7 +3382,7 @@
|
||||
./usr/man/man8/cron.8 minix-man
|
||||
./usr/man/man8/dev_mkdb.8 minix-man
|
||||
./usr/man/man8/devsize.8 minix-man
|
||||
./usr/man/man8/dhcpd.8 minix-man
|
||||
./usr/man/man8/dhcpd.8 minix-man obsolete
|
||||
./usr/man/man8/diskctl.8 minix-man
|
||||
./usr/man/man8/fbdctl.8 minix-man
|
||||
./usr/man/man8/fdisk.8 minix-man
|
||||
|
@ -458,7 +458,6 @@ install-etc-files-safe: .PHONY .MAKE check_DESTDIR MAKEDEV
|
||||
.else # LSC Minix Specific
|
||||
.for owner group mode sdir tdir files in \
|
||||
${BINOWN} ${BINGRP} ${BINMODE} ${NETBSDSRCDIR}/etc/usr/ ${DESTDIR}/usr/etc/ daily \
|
||||
${BINOWN} ${BINGRP} ${BINMODE} ${NETBSDSRCDIR}/etc/usr/ ${DESTDIR}/usr/etc/ dhcptags.conf \
|
||||
${BINOWN} ${BINGRP} ${BINMODE} ${NETBSDSRCDIR}/etc/usr/ ${DESTDIR}/usr/etc/ rc \
|
||||
${BINOWN} ${BINGRP} ${BINMODE} ${NETBSDSRCDIR}/etc/ ${DESTDIR}/usr/lib/ crontab \
|
||||
${BINOWN} ${BINGRP} ${BINMODE} ${NETBSDSRCDIR}/etc/ ${DESTDIR}/etc/ system.conf \
|
||||
|
@ -1,63 +0,0 @@
|
||||
# A list of all tags mentioned in RFC-1533.
|
||||
|
||||
tag 1 netmask ip 1 1;
|
||||
tag 2 zoneoffset number 4 1;
|
||||
tag 3 gateway ip 1 0;
|
||||
tag 4 timeserver ip 1 0;
|
||||
tag 5 nameserver ip 1 0;
|
||||
tag 6 DNSserver ip 1 0;
|
||||
tag 7 logserver ip 1 0;
|
||||
tag 8 cookieserver ip 1 0;
|
||||
tag 9 LPR ip 1 0;
|
||||
tag 10 impress ip 1 0;
|
||||
tag 11 resource ip 1 0;
|
||||
tag 12 hostname ascii 1 0;
|
||||
tag 13 bootfilesize number 2 1;
|
||||
tag 14 coredump ip 1 0;
|
||||
tag 15 domain ascii 1 0;
|
||||
tag 16 swapserver ip 1 1;
|
||||
tag 17 rootpath ascii 1 0;
|
||||
tag 18 extensions ascii 1 0;
|
||||
tag 19 IPforwarding boolean 1 1;
|
||||
tag 20 IPnonlocalsource boolean 1 1;
|
||||
tag 21 IPpolicyfilter ip 2 0;
|
||||
tag 22 IPmaxreassembly number 2 1;
|
||||
tag 23 IPTTL number 1 1;
|
||||
tag 24 IPMTUaging number 4 1;
|
||||
tag 25 IPMTUplateau number 2 0;
|
||||
tag 26 IPMTU number 2 1;
|
||||
tag 27 IPsublocal boolean 1 1;
|
||||
tag 28 IPbroadcast ip 1 1;
|
||||
tag 29 IPmaskdiscovery boolean 1 1;
|
||||
tag 30 IPmasksupplier boolean 1 1;
|
||||
tag 31 IPdiscovery boolean 1 1;
|
||||
tag 32 IPsolicitation ip 1 1;
|
||||
tag 33 IPstaticroute ip 2 0;
|
||||
tag 34 ARPtrailer boolean 1 1;
|
||||
tag 35 ARPtimeout number 4 1;
|
||||
tag 36 ETHencapsulation boolean 1 1;
|
||||
tag 37 TCPTTL number 1 1;
|
||||
tag 38 TCPkeepaliveinterval number 4 1;
|
||||
tag 39 TCPkeepalivegarbage boolean 1 1;
|
||||
tag 40 NISdomain ascii 1 0;
|
||||
tag 41 NISserver ip 1 0;
|
||||
tag 42 NTPserver ip 1 0;
|
||||
tag 43 VENDOR octet 1 0;
|
||||
tag 44 NetBIOSNS ip 1 0;
|
||||
tag 45 NetBIOSdgram ip 1 0;
|
||||
tag 46 NetBIOSnodetype number 1 1;
|
||||
tag 47 NetBIOSscope octet 1 0;
|
||||
tag 48 Xfontserver ip 1 0;
|
||||
tag 49 XDM ip 1 0;
|
||||
tag 50 DHCPreqip ip 1 1;
|
||||
tag 51 DHCPlease number 4 1;
|
||||
tag 52 DHCPoverload number 1 1;
|
||||
tag 53 DHCPtype number 1 1;
|
||||
tag 54 DHCPserverID ip 1 1;
|
||||
tag 55 DHCPreqpar number 1 0;
|
||||
tag 56 DHCPmessage ascii 1 0;
|
||||
tag 57 DHCPsize number 2 1;
|
||||
tag 58 DHCPrenewal number 4 1;
|
||||
tag 59 DHCPrebinding number 4 1;
|
||||
tag 60 DHCPclassID ascii 1 0;
|
||||
tag 61 DHCPclientID octet 1 0;
|
@ -8,7 +8,7 @@
|
||||
CPPFLAGS.fslib.c+= -I${NETBSDSRCDIR}/minix/fs
|
||||
CPPFLAGS.fsversion.c+= -I${NETBSDSRCDIR}/minix/fs
|
||||
|
||||
SRCS+= dhcp_gettag.c dhcp_settag.c fsversion.c gcov.c itoa.c \
|
||||
SRCS+= fsversion.c gcov.c itoa.c \
|
||||
oneC_sum.c read_tsc_64.c servxcheck.c fslib.c
|
||||
|
||||
.endif # defined(__MINIX)
|
||||
|
@ -6,7 +6,7 @@ SUBDIR= arp at backup \
|
||||
cawf cdprobe \
|
||||
cleantmp \
|
||||
compress crc cron crontab \
|
||||
DESCRIBE devmand devsize dhcpd \
|
||||
DESCRIBE devmand devsize \
|
||||
dhrystone \
|
||||
eject \
|
||||
fix format fsck.mfs \
|
||||
|
@ -1,7 +0,0 @@
|
||||
# Makefile for dhcpd.
|
||||
|
||||
PROG= dhcpd
|
||||
SRCS= dhcpd.c tags.c devices.c ether.c
|
||||
MAN= dhcpd.8 dhcp.conf.5
|
||||
|
||||
.include <bsd.prog.mk>
|
@ -1,26 +0,0 @@
|
||||
/* arp.h - Address Resolution Protocol packet format.
|
||||
* Author: Kees J. Bot
|
||||
* 16 Dec 2000
|
||||
*/
|
||||
#ifndef ARP_H
|
||||
#define ARP_H
|
||||
|
||||
typedef struct arp46 {
|
||||
ether_addr_t dstaddr;
|
||||
ether_addr_t srcaddr;
|
||||
ether_type_t ethtype; /* ARP_PROTO. */
|
||||
u16_t hdr, pro; /* ARP_ETHERNET & ETH_IP_PROTO. */
|
||||
u8_t hln, pln; /* 6 & 4. */
|
||||
u16_t op; /* ARP_REQUEST or ARP_REPLY. */
|
||||
ether_addr_t sha; /* Source hardware address. */
|
||||
u8_t spa[4]; /* Source protocol address. */
|
||||
ether_addr_t tha; /* Likewise for the target. */
|
||||
u8_t tpa[4];
|
||||
char padding[60 - (4*6 + 2*4 + 4*2 + 2*1)];
|
||||
} arp46_t;
|
||||
|
||||
#define ARP_ETHERNET 1 /* ARP on Ethernet. */
|
||||
#define ARP_REQUEST 1 /* A request for an IP address. */
|
||||
#define ARP_REPLY 2 /* A reply to a request. */
|
||||
|
||||
#endif /* ARP_H */
|
@ -1,347 +0,0 @@
|
||||
/* devices.c - Handle network devices.
|
||||
* Author: Kees J. Bot
|
||||
* 11 Jun 1999
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/asynchio.h>
|
||||
#include <net/hton.h>
|
||||
#include <net/gen/in.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <net/gen/ether.h>
|
||||
#include <net/gen/eth_hdr.h>
|
||||
#include <net/gen/eth_io.h>
|
||||
#include <net/gen/ip_hdr.h>
|
||||
#include <net/gen/ip_io.h>
|
||||
#include <net/gen/udp.h>
|
||||
#include <net/gen/udp_hdr.h>
|
||||
#include <net/gen/udp_io.h>
|
||||
#include <net/gen/dhcp.h>
|
||||
#include "dhcpd.h"
|
||||
|
||||
void get_buf(buf_t **bp)
|
||||
{
|
||||
/* Allocate and return a buffer pointer iff *bp == nil. */
|
||||
if (*bp != nil) {
|
||||
/* Already has one. */
|
||||
} else {
|
||||
/* Get one from the heap. */
|
||||
buf_t *new= allocate(sizeof(*new));
|
||||
new->dhcp= (dhcp_t *) (new->buf + sizeof(eth_hdr_t)
|
||||
+ sizeof(ip_hdr_t) + sizeof(udp_hdr_t));
|
||||
new->udpio= ((udp_io_hdr_t *) new->dhcp) - 1;
|
||||
new->udp= ((udp_hdr_t *) new->dhcp) - 1;
|
||||
new->ip= ((ip_hdr_t *) new->udp) - 1;
|
||||
new->eth= ((eth_hdr_t *) new->ip) - 1;
|
||||
*bp= new;
|
||||
}
|
||||
}
|
||||
|
||||
void put_buf(buf_t **bp)
|
||||
{
|
||||
/* Return a buffer to the heap. */
|
||||
if (*bp != nil) {
|
||||
free(*bp);
|
||||
*bp= nil;
|
||||
}
|
||||
}
|
||||
|
||||
void give_buf(buf_t **dbp, buf_t **sbp)
|
||||
{
|
||||
/* Hand over a buffer to another variable. */
|
||||
put_buf(dbp);
|
||||
*dbp= *sbp;
|
||||
*sbp= nil;
|
||||
}
|
||||
|
||||
#define N_FDS 16 /* Minix can go async on many fds. */
|
||||
|
||||
static fd_t fds[N_FDS]; /* List of open descriptors. */
|
||||
static struct network *fdwaitq; /* Queue of nets waiting for fds. */
|
||||
|
||||
network_t *newnetwork(void)
|
||||
{
|
||||
/* Create and initialize a network structure. */
|
||||
network_t *new;
|
||||
|
||||
new= allocate(sizeof(*new));
|
||||
memset(new, 0, sizeof(*new));
|
||||
new->hostname= nil;
|
||||
new->solicit= NEVER;
|
||||
new->sol_ct= -1;
|
||||
return new;
|
||||
}
|
||||
|
||||
void closefd(fd_t *fdp)
|
||||
{
|
||||
/* Close a descriptor. */
|
||||
if (fdp->fdtype != FT_CLOSED) {
|
||||
asyn_close(&asyn, fdp->fd);
|
||||
close(fdp->fd);
|
||||
fdp->fdtype= FT_CLOSED;
|
||||
fdp->since= 0;
|
||||
put_buf(&fdp->bp);
|
||||
if (debug >= 3) printf("%s: Closed\n", fdp->device);
|
||||
}
|
||||
}
|
||||
|
||||
static void timeout(int signum)
|
||||
{
|
||||
/* nothing to do, ioctl will be aborted automatically */
|
||||
if (alarm(1) == (unsigned int)-1) fatal("alarm(1)");
|
||||
}
|
||||
|
||||
int opendev(network_t *np, fdtype_t fdtype, int compete)
|
||||
{
|
||||
/* Make sure that a network has the proper device open and configured.
|
||||
* Return true if this is made so, or false if the device doesn't exist.
|
||||
* If compete is true then the caller competes for old descriptors.
|
||||
* The errno value is EAGAIN if we're out of descriptors.
|
||||
*/
|
||||
fd_t *fdp, *fdold;
|
||||
time_t oldest;
|
||||
nwio_ethstat_t ethstat;
|
||||
nwio_ethopt_t ethopt;
|
||||
nwio_ipopt_t ipopt;
|
||||
nwio_udpopt_t udpopt;
|
||||
network_t **pqp;
|
||||
static char devbytype[][4] = { "", "eth", "ip", "udp", "udp" };
|
||||
|
||||
/* Don't attempt to open higher level devices if not bound. */
|
||||
if (!(np->flags & NF_BOUND) && fdtype > FT_ETHERNET) {
|
||||
errno= EAGAIN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if already open / Find the oldest descriptor. */
|
||||
fdold= nil;
|
||||
oldest= NEVER;
|
||||
for (fdp= fds; fdp < arraylimit(fds); fdp++) {
|
||||
if (fdp->n == np->n && fdp->fdtype == fdtype) {
|
||||
/* Already open. */
|
||||
np->fdp= fdp;
|
||||
return 1;
|
||||
}
|
||||
if (fdp->since <= oldest) { fdold= fdp; oldest= fdp->since; }
|
||||
}
|
||||
|
||||
/* None free? Then wait for one to get old if so desired. */
|
||||
if (fdold->fdtype != FT_CLOSED && !compete) {
|
||||
errno= EAGAIN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(np->flags & NF_WAIT)) {
|
||||
for (pqp= &fdwaitq; *pqp != nil; pqp= &(*pqp)->wait) {}
|
||||
*pqp= np;
|
||||
np->wait= nil;
|
||||
np->flags |= NF_WAIT;
|
||||
}
|
||||
|
||||
/* We allow a net to keep a descriptor for half of the fast period. */
|
||||
oldest += DELTA_FAST/2;
|
||||
|
||||
if (fdwaitq != np || (fdold->fdtype != FT_CLOSED && oldest > now)) {
|
||||
/* This net is not the first in the queue, or the oldest isn't
|
||||
* old enough. Forget it for now.
|
||||
*/
|
||||
if (oldest < event) event= oldest;
|
||||
errno= EAGAIN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The oldest is mine. */
|
||||
np->flags &= ~NF_WAIT;
|
||||
fdwaitq= np->wait;
|
||||
closefd(fdold);
|
||||
|
||||
/* Open the proper device in the proper mode. */
|
||||
fdp= fdold;
|
||||
fdp->n= np->n;
|
||||
if (lwip && (fdtype == FT_ETHERNET || fdtype == FT_ICMP))
|
||||
sprintf(fdp->device, "/dev/ip");
|
||||
else
|
||||
sprintf(fdp->device, "/dev/%s%d", devbytype[fdtype], np->n);
|
||||
np->fdp= fdp;
|
||||
|
||||
if ((fdp->fd= open(fdp->device, O_RDWR)) < 0) {
|
||||
if (errno == ENOENT || errno == ENODEV || errno == ENXIO) return 0;
|
||||
fatal(fdp->device);
|
||||
}
|
||||
|
||||
switch (fdtype) {
|
||||
case FT_ETHERNET:
|
||||
if (lwip) {
|
||||
nwio_ipopt_t ipopt;
|
||||
int result;
|
||||
char ethdev[64];
|
||||
int efd;
|
||||
|
||||
sprintf(ethdev, "/dev/eth%d", np->n);
|
||||
|
||||
if ((efd = open(fdp->device, O_RDWR)) < 0) {
|
||||
if (errno == ENOENT || errno == ENODEV ||
|
||||
errno == ENXIO)
|
||||
return 0;
|
||||
fatal(ethdev);
|
||||
}
|
||||
|
||||
if (ioctl(efd, NWIOGETHSTAT, ðstat) < 0) {
|
||||
/* Not an Ethernet. */
|
||||
close(efd);
|
||||
return 0;
|
||||
}
|
||||
close(efd);
|
||||
|
||||
np->eth= ethstat.nwes_addr;
|
||||
|
||||
ipopt.nwio_flags= NWIO_COPY | NWIO_PROTOSPEC;
|
||||
ipopt.nwio_proto= 17; /* UDP */
|
||||
result= ioctl (fdp->fd, NWIOSIPOPT, &ipopt);
|
||||
if (result<0)
|
||||
perror("ioctl (NWIOSIPOPT)"), exit(1);
|
||||
|
||||
break;
|
||||
}
|
||||
/* Cannot use NWIOGETHSTAT in non-blocking mode due to a race between
|
||||
* the reply from the ethernet driver and the cancel message from VFS
|
||||
* for reaching inet. Hence, a signal is used to interrupt NWIOGETHSTAT
|
||||
* in case the driver isn't ready yet.
|
||||
*/
|
||||
if (signal(SIGALRM, timeout) == SIG_ERR) fatal("signal(SIGALRM)");
|
||||
if (alarm(1) == (unsigned int)-1) fatal("alarm(1)");
|
||||
if (ioctl(np->fdp->fd, NWIOGETHSTAT, ðstat) < 0) {
|
||||
/* Not an Ethernet. */
|
||||
close(fdp->fd);
|
||||
return 0;
|
||||
}
|
||||
if (alarm(0) == (unsigned int)-1) fatal("alarm(0)");
|
||||
np->eth= ethstat.nwes_addr;
|
||||
ethopt.nweo_flags= NWEO_COPY | NWEO_EN_LOC | NWEO_EN_BROAD
|
||||
| NWEO_REMANY | NWEO_TYPEANY | NWEO_RWDATALL;
|
||||
|
||||
if (ioctl(fdp->fd, NWIOSETHOPT, ðopt) < 0) {
|
||||
fprintf(stderr, "%s: %s: Unable to set eth options: %s\n",
|
||||
program, fdp->device, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case FT_ICMP:
|
||||
ipopt.nwio_flags= NWIO_COPY | NWIO_EN_LOC | NWIO_EN_BROAD
|
||||
| NWIO_REMANY | NWIO_PROTOSPEC
|
||||
| NWIO_HDR_O_SPEC | NWIO_RWDATALL;
|
||||
ipopt.nwio_tos= 0;
|
||||
ipopt.nwio_ttl= 1;
|
||||
ipopt.nwio_df= 0;
|
||||
ipopt.nwio_hdropt.iho_opt_siz= 0;
|
||||
ipopt.nwio_proto= IPPROTO_ICMP;
|
||||
|
||||
if (ioctl(fdp->fd, NWIOSIPOPT, &ipopt) < 0) {
|
||||
fprintf(stderr, "%s: %s: Unable to set IP options: %s\n",
|
||||
program, fdp->device, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case FT_BOOTPC:
|
||||
if (lwip) {
|
||||
struct sockaddr_in si_me;
|
||||
|
||||
close(fdp->fd);
|
||||
fdp->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (fdp->fd < 0)
|
||||
return 0;
|
||||
memset((char *) &si_me, 0, sizeof(si_me));
|
||||
si_me.sin_family = AF_INET;
|
||||
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
si_me.sin_port = htons(port_client);
|
||||
if (bind(fdp->fd, (struct sockaddr *) &si_me,
|
||||
sizeof(si_me)) == -1) {
|
||||
close(fdp->fd);
|
||||
printf("DHCP : cannot bind client socket to port %d\n",
|
||||
port_client);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
udpopt.nwuo_flags= NWUO_COPY | NWUO_EN_LOC | NWUO_EN_BROAD
|
||||
| NWUO_RP_ANY | NWUO_RA_ANY | NWUO_RWDATALL
|
||||
| NWUO_DI_IPOPT | NWUO_LP_SET;
|
||||
udpopt.nwuo_locport= port_client;
|
||||
goto udp;
|
||||
|
||||
case FT_BOOTPS:
|
||||
udpopt.nwuo_flags= NWUO_EXCL | NWUO_EN_LOC | NWUO_EN_BROAD
|
||||
| NWUO_RP_ANY | NWUO_RA_ANY | NWUO_RWDATALL
|
||||
| NWUO_DI_IPOPT | NWUO_LP_SET;
|
||||
udpopt.nwuo_locport= port_server;
|
||||
udp:
|
||||
if (ioctl(fdp->fd, NWIOSUDPOPT, &udpopt) == -1) {
|
||||
fprintf(stderr, "%s: %s: Unable to set UDP options: %s\n",
|
||||
program, fdp->device, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
|
||||
default:;
|
||||
}
|
||||
|
||||
fdp->fdtype= fdtype;
|
||||
fdp->since= now;
|
||||
if (debug >= 3) printf("%s: Opened\n", fdp->device);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void closedev(network_t *np, fdtype_t fdtype)
|
||||
{
|
||||
/* We no longer need a given type of device to be open. */
|
||||
fd_t *fdp;
|
||||
|
||||
for (fdp= fds; fdp < arraylimit(fds); fdp++) {
|
||||
if (fdp->n == np->n && (fdp->fdtype == fdtype || fdtype == FT_ALL)) {
|
||||
closefd(fdp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char *ipdev(int n)
|
||||
{
|
||||
/* IP device for network #n. */
|
||||
static char device[sizeof("/dev/ipNNN")];
|
||||
|
||||
sprintf(device, "/dev/ip%d", n);
|
||||
return device;
|
||||
}
|
||||
|
||||
void set_ipconf(char *device, ipaddr_t ip, ipaddr_t mask, unsigned mtu)
|
||||
{
|
||||
/* Set IP address and netmask of an IP device. */
|
||||
int fd;
|
||||
nwio_ipconf_t ipconf;
|
||||
|
||||
if (test > 0) return;
|
||||
|
||||
if ((fd= open(device, O_RDWR)) < 0) fatal(device);
|
||||
ipconf.nwic_flags= NWIC_IPADDR_SET | NWIC_NETMASK_SET;
|
||||
ipconf.nwic_ipaddr= ip;
|
||||
ipconf.nwic_netmask= mask;
|
||||
#ifdef NWIC_MTU_SET
|
||||
if (mtu != 0) {
|
||||
ipconf.nwic_flags |= NWIC_MTU_SET;
|
||||
ipconf.nwic_mtu= mtu;
|
||||
}
|
||||
#endif
|
||||
if (ioctl(fd, NWIOSIPCONF, &ipconf) < 0) fatal(device);
|
||||
close(fd);
|
||||
}
|
@ -1,498 +0,0 @@
|
||||
.TH DHCP.CONF 5
|
||||
.SH NAME
|
||||
dhcp.conf \- dynamic host configuration protocol configuration
|
||||
.SH SYNOPSIS
|
||||
.B /etc/dhcp.conf
|
||||
.SH DESCRIPTION
|
||||
.de SP
|
||||
.if t .sp 0.4
|
||||
.if n .sp
|
||||
..
|
||||
The file
|
||||
.B /etc/dhcp.conf
|
||||
contains the configuration for the DHCP client/server program
|
||||
.BR dhcpd .
|
||||
This text is a long summation of all the elements that can be found in this
|
||||
configuration file. For a more "just tell me what to do" approach see
|
||||
.BR boot (8).
|
||||
.PP
|
||||
The syntax used is that of the common configuration file described in
|
||||
.BR configfile (5).
|
||||
.PP
|
||||
To find information for a client we first need its IP address. Occasionally
|
||||
this IP address is already known (the special "INFORM" query), but normally
|
||||
we have to make a first pass through the configuration file for a
|
||||
.B client
|
||||
entry. If that fails then we use an IP address from the pool file. If we
|
||||
now have an IP address then the real information gathering can begin.
|
||||
.PP
|
||||
The DHCP daemon reads the configuration file from beginning to end and
|
||||
gathers all information that matches, and information from all macros that
|
||||
are mentioned within the elements that match. If we end up with DHCP
|
||||
information that includes at least a netmask definition, and is good for the
|
||||
network the request came in from, then it is returned to the client. If a
|
||||
DHCP tag is specified twice then the last one wins.
|
||||
.PP
|
||||
In the description below we use [ and ] to denote optional things, and | to
|
||||
show a choice between two things.
|
||||
.PP
|
||||
Client IDs can be either ordinary Ethernet addresses, that are treated as a
|
||||
seven octet string (01 followed by the Ethernet address), or any random
|
||||
octet string in hexadecimal.
|
||||
.PP
|
||||
IP addresses can be simply that, or host names. These host names are
|
||||
searched in
|
||||
.B /etc/hosts
|
||||
by
|
||||
.B dhcpd
|
||||
itself using a domain based prefix match, i.e. you can use "flotsam" for
|
||||
"flotsam.example.com", but not "alpha" for "alphabeta". Once the program
|
||||
decides to be a server it will also look up names normally in the DNS.
|
||||
If a host has more than one IP address then the address on the network the
|
||||
query was seen on is used.
|
||||
.PP
|
||||
Case isn't important in the configuration file, "client", "CLIENT" and
|
||||
"ClIeNt" are all treated the same.
|
||||
.PP
|
||||
Some elements may optionally name a macro or a curly braces enclosed
|
||||
parameter list of more elements. If the element matches then the data
|
||||
in the macro body or parameter list is gathered.
|
||||
.PP
|
||||
The following elements can be used:
|
||||
.PP
|
||||
.B client
|
||||
.I client-ID
|
||||
.RB [ ip #]
|
||||
.I host
|
||||
.RI [ macro |{ params }];
|
||||
.PP
|
||||
.RS
|
||||
Defines a client with a given client ID that is to have the IP address
|
||||
denoted by
|
||||
.I host .
|
||||
On the first pass only the client ID is matched looking for an IP address
|
||||
that lies on the network the request came in on. On the
|
||||
information gathering pass both client ID and IP address must match. If
|
||||
a machine has the same Ethernet address on two or more interfaces then the
|
||||
IP address given out is the one on the same network as the request came in
|
||||
on. The optional interface name
|
||||
.RB ( ip #)
|
||||
must be used if the DHCP daemon is gathering data for itself at boot time
|
||||
to differentiate interfaces with the same ethernet addresses. This is
|
||||
only necessary under Minix-vmd when ethernets on different VLANs share
|
||||
the same physical ethernet. The interface name is only used for a machine's
|
||||
own networks, it ignored on entries for other hosts.
|
||||
.RE
|
||||
.PP
|
||||
.B class
|
||||
.IR class-name " ..."
|
||||
.IR macro |{ params };
|
||||
.PP
|
||||
.RS
|
||||
Includes the macro or parameters if one of the class names is matched. A
|
||||
host normally includes a class ID in its request. MINIX 3 and Minix-vmd
|
||||
use "Minix" as the class name. For Windows the class ID starts with
|
||||
"MSFT", and Solaris' starts with "SUNW".
|
||||
(Use
|
||||
.B dhcpd \-d3
|
||||
to find out what the full IDs are exactly.) The class names are matched if a
|
||||
.I class-name
|
||||
is a prefix of the class ID sent by the client.
|
||||
.RE
|
||||
.PP
|
||||
.B host
|
||||
.I host-spec
|
||||
.IR macro |{ params };
|
||||
.PP
|
||||
.RS
|
||||
Includes the macro or parameters if the IP address of the client matches the
|
||||
host specification. This can either be an ordinary hostname, or a netblock
|
||||
in CIDR notation, e.g. 172.35.0.0/16. The example includes all IP addresses
|
||||
whose top 16 bits are the same as the top 16 bits of 172.35.0.0. Such a
|
||||
netblock automatically defines a netmask (255.255.0.0 in the example) if no
|
||||
netmask has been specified yet.
|
||||
.RE
|
||||
.PP
|
||||
.B interface
|
||||
.BR ip #
|
||||
.I host
|
||||
.RI [ macro |{ params }];
|
||||
.PP
|
||||
.RS
|
||||
Makes
|
||||
.B dhcpd
|
||||
set the IP address of interface
|
||||
.BR ip #
|
||||
(where # is a number) to the IP address denoted by
|
||||
.IR host .
|
||||
This element should only be used for interfaces that are not true Ethernets,
|
||||
and so do not have a unique Ethernet address that can be used for a client
|
||||
element. If the machine has at least one true Ethernet then all interface
|
||||
elements should be added to the parameter list of a host or client element
|
||||
for that Ethernet interface. This binds them to that machine and allows a
|
||||
single configuration file to be shared among machines. Especially a server
|
||||
should never have "free" interface elements. The macro or parameters are
|
||||
only evaluated if data is gathered for the given interface. (Note that they
|
||||
will be hidden by a client element for another interface.)
|
||||
.RE
|
||||
.PP
|
||||
.B macro
|
||||
.IR macro-name ;
|
||||
.PP
|
||||
.RS
|
||||
Include the parameter list of the macro named
|
||||
.I macro-name
|
||||
defined elsewhere. (This means that "host flotsam stuff" is just short
|
||||
for "host flotsam { macro stuff; }".)
|
||||
.RE
|
||||
.PP
|
||||
.B macro
|
||||
.I macro-name
|
||||
.RI { params };
|
||||
.PP
|
||||
.RS
|
||||
Defines a macro with the given parameter list. Whenever the macro is used
|
||||
the parameter list is substituted instead. A macro can not be defined
|
||||
within another parameter list.
|
||||
.RE
|
||||
.PP
|
||||
.B option
|
||||
.RB [ ip #]
|
||||
.B server
|
||||
.RB [ inform ];
|
||||
.br
|
||||
.B option
|
||||
.RB [ ip #]
|
||||
.B relay
|
||||
.IR host ;
|
||||
.br
|
||||
.B option
|
||||
.RB [ ip #]
|
||||
.BR possessive ;
|
||||
.br
|
||||
.B option
|
||||
.RB [ ip #]
|
||||
.B hostname
|
||||
.IR name ;
|
||||
.PP
|
||||
.RS
|
||||
Makes
|
||||
.B dhcpd
|
||||
set special options for the interface that it is gathering data for, or the
|
||||
interface denoted by the optional
|
||||
.BR ip #
|
||||
argument. The options are:
|
||||
.SP
|
||||
.B server
|
||||
.RB [ inform ]
|
||||
.RS
|
||||
Be a DHCP server on the network connected to the interface. Add the word
|
||||
.B inform
|
||||
if DHCPINFORM requests must be answered for hosts we don't have an address
|
||||
for.
|
||||
.RE
|
||||
.SP
|
||||
.B relay
|
||||
.I host
|
||||
.RS
|
||||
Be a DHCP relay to the indicated host.
|
||||
.RE
|
||||
.SP
|
||||
.B possessive
|
||||
.RS
|
||||
Do not disable the interface if the DHCP lease expires. Useful if the
|
||||
DHCP server of the provider is unreliable, crashing a lot and causing the
|
||||
lease to expire. (Think twice before turning this option on. You have to
|
||||
be absolutely sure that it's the DHCP server that's the culprit and not
|
||||
a flaky network. You don't want an IP address conflict to be your fault.)
|
||||
.RE
|
||||
.SP
|
||||
.B hostname
|
||||
.I name
|
||||
.RS
|
||||
Use the given name as our hostname in the DHCP queries. Some sites key on
|
||||
that bit of information instead of a client ID.
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
.B tag
|
||||
.I number name type granularity
|
||||
.IR max ;
|
||||
.PP
|
||||
.RS
|
||||
Defines a DHCP tag for the given tag number and gives it a name. The name can
|
||||
be used in the configuration file to set tag values when gathering data.
|
||||
The
|
||||
.I type
|
||||
field can be one of
|
||||
.BR ascii ,
|
||||
.BR boolean ,
|
||||
.BR ip ,
|
||||
.BR number
|
||||
or
|
||||
.BR octet
|
||||
to specify the type of the tag as a simple string, a boolean, an IP address,
|
||||
a number, or a string of octet values.
|
||||
The
|
||||
.I granularity
|
||||
field specifies that that number of items must be given or a multiple
|
||||
thereof, unless the type is a number, then it is the size of the number (1,
|
||||
2 or 4).
|
||||
The
|
||||
.I max
|
||||
field tells the maximum number of items that may be used with the tag, with
|
||||
0 meaning "unlimited".
|
||||
.SP
|
||||
Three tags, the ones that MINIX 3 really cares about, have been predefined,
|
||||
and there are also a few pseudotags predefined for the static fields in a
|
||||
DHCP packet that one may want to set:
|
||||
.SP
|
||||
.RS
|
||||
.nf
|
||||
tag ? siaddr ip 1 1;
|
||||
tag ? sname ascii 1 64;
|
||||
tag ? file ascii 1 128;
|
||||
tag 1 netmask ip 1 1;
|
||||
tag 3 gateway ip 1 0;
|
||||
tag 6 DNSserver ip 1 0;
|
||||
.fi
|
||||
.RE
|
||||
.SP
|
||||
The file
|
||||
.B /usr/etc/dhcptags.conf
|
||||
contains tag definitions for all standard DHCP tags. It is wise to include
|
||||
this file at the top of any DHCP configuration file.
|
||||
.RE
|
||||
.PP
|
||||
.B no
|
||||
.IR tag-name ;
|
||||
.PP
|
||||
.RS
|
||||
Removes a tag with the given name from the data gathered at this point.
|
||||
Useful if one host is different from all others, for instance if it doesn't
|
||||
need a gateway definition, because it happens to be the gateway.
|
||||
.RE
|
||||
.PP
|
||||
.IR "ascii-tag string" ;
|
||||
.PP
|
||||
.RS
|
||||
Adds an ASCII tag to the gathered data. The string can be a simple word, or
|
||||
a quoted string.
|
||||
.RE
|
||||
.PP
|
||||
.I boolean-tag
|
||||
.BR false | true ;
|
||||
.PP
|
||||
.RS
|
||||
Set a boolean tag to be false or true. (Encoded as a octet of value 0 or 1.
|
||||
Note that if you prefer to use 0 or 1 instead of false or true then you can
|
||||
define a boolean tag as a size 1 number instead.)
|
||||
.RE
|
||||
.PP
|
||||
.IR "ip-tag host" " ...;"
|
||||
.PP
|
||||
.RS
|
||||
Sets a tag that needs one or more IP addresses. The host names are
|
||||
translated as usual. To make it easier to specify netmasks one can use a
|
||||
slash followed by a number, e.g.
|
||||
.BR "netmask /27" ,
|
||||
which is a handy alternative for
|
||||
.BR "netmask 255.255.255.224" .
|
||||
.RE
|
||||
.PP
|
||||
.IR "number-tag number" " ...;"
|
||||
.PP
|
||||
.RS
|
||||
Set a number tag.
|
||||
.RE
|
||||
.PP
|
||||
.IR "octet-tag hexdigits" ;
|
||||
.PP
|
||||
.RS
|
||||
Set an octet string tag.
|
||||
.I Hexdigits
|
||||
is a series of hexadecimal digits that are two by two used to set the
|
||||
octets.
|
||||
.RE
|
||||
.PP
|
||||
.SH EXAMPLE
|
||||
As an example the DHCP configuration used by the author of this document is
|
||||
included. His network at home consists of a number of PCs, an ISDN router
|
||||
named rhone and a PC named saone serving as router/tunnel to/via a cable
|
||||
ISP. Both the rhone and the saone connect the home net to the network of
|
||||
the Vrije Universiteit, but the rhone is only active if the cable doesn't
|
||||
work.
|
||||
.PP
|
||||
The saone is a DHCP server, and one of the ordinary PCs is a backup DHCP
|
||||
server. Both use the same configuration file, which is added below, with
|
||||
extra commentary introduced by
|
||||
.B ##
|
||||
at a deeper indent level:
|
||||
.RS
|
||||
.de xS \" Example start
|
||||
.sp
|
||||
.nf
|
||||
.ft C
|
||||
..
|
||||
.de xE \" Example end
|
||||
.fi
|
||||
.ft R
|
||||
..
|
||||
.de cS \" Commentary start
|
||||
.sp
|
||||
.in +12m
|
||||
.ti -\w'## 'u
|
||||
##\ \c
|
||||
..
|
||||
.de cE \" Commentary end
|
||||
.in -12m
|
||||
..
|
||||
.xS
|
||||
.ta +8m +16m
|
||||
include /usr/etc/dhcptags.conf;
|
||||
.xE
|
||||
.cS
|
||||
With the help of the tag definitions we can use tags like "DHCPlease".
|
||||
.cE
|
||||
.xS
|
||||
host 130.37.102.64/27 {
|
||||
DNSserver saone darask;
|
||||
host 130.37.102.88/29 { DHCPlease 259200; }
|
||||
};
|
||||
.xE
|
||||
.cS
|
||||
This defines the network 130.37.102.64/27, with netmask 255.255.255.224
|
||||
(implicit from the network definition). The DNS servers for this net are
|
||||
saone and darask. A smaller subrange of addresses is used as an address
|
||||
pool on the saone, so a lease of 259200 seconds (3 days) is defined. The
|
||||
netmask is still /27, as set by the outer network definition.
|
||||
.cE
|
||||
.xS
|
||||
host 130.37.102.248/30 {};
|
||||
.xE
|
||||
.cS
|
||||
A network of two addresses for routing tests.
|
||||
.cE
|
||||
.xS
|
||||
host saone {
|
||||
option server;
|
||||
option ip1 possessive;
|
||||
interface ip2 saone-net2;
|
||||
DNSserver 130.37.24.3 130.37.24.6;
|
||||
};
|
||||
.xE
|
||||
.cS
|
||||
With the network definitions done we turn our attention to the hosts. Saone
|
||||
is a DHCP server on its main interface. The second interface
|
||||
.RB ( ip1 )
|
||||
is connected to the cable modem. It gets its address from the cableco's
|
||||
DHCP server, and if that server decides to go deaf then the saone keeps
|
||||
the interface up ("possessive") even if the lease expires. The pseudo IP
|
||||
interface
|
||||
.B ip2
|
||||
is set to the IP address of
|
||||
.BR saone-net2 ,
|
||||
one side of the encrypted tunnel over the cable to a Minix-vmd box at the VU.
|
||||
The DNS servers specified override the default setting for the network, naming
|
||||
two external servers at the VU that know the world.
|
||||
.cE
|
||||
.xS
|
||||
host darask {
|
||||
option server;
|
||||
DNSserver saone;
|
||||
class MINIX 3 { DNSserver saone 130.37.24.3 130.37.24.6; };
|
||||
};
|
||||
.xE
|
||||
.cS
|
||||
The darask is also a server, the backup for saone on the odd chance that it
|
||||
is unavailable. It uses saone and the external name servers, but only
|
||||
when it is running MINIX 3. When running Windows it only uses saone.
|
||||
.cE
|
||||
.xS
|
||||
.ta +32m +16m
|
||||
client 0:1:1b:a:68:ce darask; # NE2000
|
||||
client 0:1:1b:a:77:23 burask; # NE2000
|
||||
#lient 0:0:c0:b0:da:64 burask; # WD8013EWC
|
||||
client 8:0:5a:38:b2:f finiah; # PCMCIA NE2000
|
||||
client 0:0:c0:3a:12:10 bardelask; # WD8003
|
||||
#lient 2:60:8c:ab:8a:6d bardelask; # 3C503
|
||||
client 0:a0:c5:20:9:6d rhone;
|
||||
client 0:1:1b:a:4c:3b saone; # NE2000
|
||||
#lient 0:0:c0:fb:2:6a saone-net1; # WD8013EWC
|
||||
.xE
|
||||
.cS
|
||||
Lastly the ethernet addresses of all the hosts are listed, so that they can
|
||||
be translated to IP addresses. The lines that are commented out are for
|
||||
extra network cards that are currently unused. The last is used to connect
|
||||
to the cable modem, so it's only here because it's nice to have the ethernet
|
||||
address written down somewhere.
|
||||
.cE
|
||||
.RE
|
||||
.PP
|
||||
The host names shown above are translated by DHCP using this
|
||||
.BR /etc/hosts :
|
||||
.RS
|
||||
.xS
|
||||
.ta +\w'130.37.102.249mm'u
|
||||
604800 %ttl
|
||||
2419200 %stale
|
||||
|
||||
130.37.102.65 darask.kjb.upwind.org
|
||||
130.37.102.66 burask.kjb.upwind.org
|
||||
130.37.102.67 finiah.kjb.upwind.org
|
||||
130.37.102.68 bardelask.kjb.upwind.org
|
||||
130.37.102.69 roniah.kjb.upwind.org
|
||||
|
||||
130.37.102.70 saone.kjb.upwind.org
|
||||
130.37.102.2 saone-net2.kjb.upwind.org
|
||||
|
||||
130.37.102.88 rhone.kjb.upwind.org
|
||||
130.37.102.89 dyn89.kjb.upwind.org
|
||||
130.37.102.90 dyn90.kjb.upwind.org
|
||||
130.37.102.91 dyn91.kjb.upwind.org
|
||||
130.37.102.92 dyn92.kjb.upwind.org
|
||||
130.37.102.93 dyn93.kjb.upwind.org
|
||||
130.37.102.94 dyn94.kjb.upwind.org
|
||||
|
||||
130.37.102.249 tst1.kjb.upwind.org
|
||||
130.37.102.250 tst2.kjb.upwind.org
|
||||
.xE
|
||||
.RE
|
||||
.SH FILES
|
||||
.TP
|
||||
.B /usr/etc/dhcptags.conf
|
||||
A supplied list of standard tag definitions as per RFC-1533. (Well, the
|
||||
tag numbers and their meaning are standard, the names are made up.)
|
||||
.SH "SEE ALSO"
|
||||
.BR RFC-2131 ,
|
||||
.BR RFC-1533 ,
|
||||
.BR configfile (5),
|
||||
.BR hosts (5),
|
||||
.BR boot (8),
|
||||
.BR dhcpd (8).
|
||||
.SH NOTES
|
||||
The amount of memory
|
||||
.B dhcpd
|
||||
needs increases with the size of configuration file. MINIX 3 can
|
||||
handle
|
||||
.B dhcptags.conf
|
||||
and a modest sized
|
||||
.BR dhcp.conf .
|
||||
You have to increase the stack size to accommodate more. (No problem under
|
||||
Minix-vmd, of course.)
|
||||
.SH NOTES
|
||||
Items that are only necessary for a certain host should only be specified
|
||||
for that host. Items for a whole network are best added to a netblock
|
||||
specification. Use class elements for a certain type of host, and macros
|
||||
for exceptions. Try to limit information as much as possibly to those hosts
|
||||
that need it. (Don't go overboard. A MINIX 3 machine won't be bothered by a
|
||||
few NetBIOS tags.)
|
||||
.PP
|
||||
DHCPINFORM requests should always be answered when being a server, but
|
||||
J. Random Sysadmin trying to diagnose problems doesn't like it when little
|
||||
MINIX 3 machines show up in a packet trace unexpectedly. It's best to be
|
||||
inconspicuous on a network you don't own.
|
||||
.SH BUGS
|
||||
There are a few too many subtle mistakes one can make.
|
||||
.SH AUTHOR
|
||||
Kees J. Bot <kjb@cs.vu.nl>
|
@ -1,208 +0,0 @@
|
||||
.TH DHCPD 8
|
||||
.SH NAME
|
||||
dhcpd \- dynamic host configuration protocol daemon
|
||||
.SH SYNOPSIS
|
||||
.in +.5i
|
||||
.ti -.5i
|
||||
.B dhdpd
|
||||
.RB [ \-qar ]
|
||||
.RB [ \-t [\fIlevel\fP]]
|
||||
.RB [ \-d [\fIlevel\fP]]
|
||||
.RB [ \-f
|
||||
.IR configfile ]
|
||||
.RB [ \-c
|
||||
.IR cachefile ]
|
||||
.RB [ \-p
|
||||
.IR poolfile ]
|
||||
.RI [ host " ...]"
|
||||
.in -.5i
|
||||
.SH DESCRIPTION
|
||||
.de SP
|
||||
.if t .sp 0.4
|
||||
.if n .sp
|
||||
..
|
||||
.B Dhcpd
|
||||
is a client and a server for the Dynamic Host Configuration Protocol. As a
|
||||
client it collects DHCP data to configure the Ethernet networks with, and as
|
||||
a server it answers DHCP queries from other machines.
|
||||
.PP
|
||||
This manual page describes the operation of
|
||||
.BR dhcpd ,
|
||||
the associated configuration file is described in
|
||||
.BR dhcp.conf (5).
|
||||
(The latter, together with
|
||||
.BR boot (8),
|
||||
is of more practical value when it comes to getting a machine's networks
|
||||
interfaces up and running. See the options section below for debugging DCHP
|
||||
problems.)
|
||||
.SS Initialization
|
||||
On a normal startup, i.e. none of the
|
||||
.BR \-q ,
|
||||
.BR \-a
|
||||
or
|
||||
.BR \-r
|
||||
options are given,
|
||||
.B dhcpd
|
||||
determines what IP devices are present, and which of those are Ethernets.
|
||||
For each network it looks for information in the configuration file as if
|
||||
it were a server answering a query for that network. If any information is
|
||||
found then the IP address is configured and the information stored in the
|
||||
cache file.
|
||||
.SS "Client Operation"
|
||||
For each still unconfigured network a DHCP DISCOVER request is broadcast on
|
||||
that network. If a DHCP OFFER reply is received then a DHCP REQUEST is
|
||||
broadcast for the IP address offered, and if a DHCP ACK is received then the
|
||||
network is configured and the information stored in the cache file.
|
||||
.PP
|
||||
If no reply is received then another query is sent after 4 seconds, and then
|
||||
again after 8 seconds, doubling each time until 64 seconds. Every 64
|
||||
seconds thereafter a request is broadcast until a reply is received.
|
||||
.PP
|
||||
Once configured the DHCP lease, rebind and renew times are computed. At the
|
||||
renew time a DHCP REQUEST is sent to the DHCP server to extend the lease.
|
||||
Normally we get an answer and refresh our information, but if no reply is
|
||||
received we wait for half the remaining time until the rebind time and keep
|
||||
retrying and halving the remaining time. When the rebind time is reached
|
||||
the DHCP REQUEST is broadcast to try and reach some other DHCP server.
|
||||
Halving the remaining time again and again until the lease expires. At that
|
||||
point we go back to square one and broadcast a DHCP DISCOVER.
|
||||
.PP
|
||||
If at any point a DHCP NAK is received we start over completely. After a
|
||||
DHCP OFFER an ARP request is transmitted just before the DHCP REQUEST to
|
||||
check if the address offered is already in use. If an ARP reply is received
|
||||
before the DHCP ACK then after the ACK we send a DHCP DECLINE to the server
|
||||
to tell that the address isn't what we want and again we start over.
|
||||
.SS "Router Discovery"
|
||||
The gateway offered by the DHCP server is made known to the TCP/IP server by
|
||||
sending an ICMP router advertisement to the local interface with a short
|
||||
lifetime and a low priority. Then up to three router solicitations are
|
||||
broadcast three seconds apart to look for a router. If a router answers
|
||||
with a router advertisement then we no longer worry about routing for that
|
||||
interface. Otherwise the router information is refreshed before it expires
|
||||
and another solicitation is sent out. This happens about twice an hour.
|
||||
.SS "Server Operation"
|
||||
Once all networks so marked are configured the daemon starts answering
|
||||
requests by other machines or relaying requests to other DHCP servers.
|
||||
DHCP requests are answered if information for a client
|
||||
can be found in the configuration file, or if a free address can be found in
|
||||
the pool file, or if a client rerequests an address it already owns.
|
||||
.PP
|
||||
If the daemon is both a server and a relay for a network then it will try
|
||||
to answer a request and only relay if it has no answer.
|
||||
.SS "Nothing more to do?"
|
||||
If the daemon finds out that all networks have an infinite lease (configured
|
||||
with a fixed address), there is no router information to keep warm, and
|
||||
it isn't a server then it simply exits.
|
||||
.SS "Asynchronous I/O?"
|
||||
MINIX 3 doesn't have the asynchronous I/O that Minix-vmd has, so under MINIX 3
|
||||
the daemon only works with one network at a time. If it's stuck on the same
|
||||
network for 32 seconds then that network is closed and another network is
|
||||
tried for 32 seconds. This usually works ok as a client, but as a server it
|
||||
can only handle one network.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B \-q
|
||||
Read and print the cache and pool file contents, showing DHCP information
|
||||
for each network, and the IP addresses in the pool with lease times and
|
||||
current/last owners of those addresses.
|
||||
.TP
|
||||
.B \-a
|
||||
Add the named hosts (or IP addresses) to the pool file.
|
||||
.TP
|
||||
.B \-r
|
||||
Remove hosts from the pool file.
|
||||
.TP
|
||||
.RB [ \-t [\fIlevel\fP]]
|
||||
Set the test level (by default 1). At test level 1 all networks are seen as
|
||||
unconfigured, will not be configured and no data will be put in the cache.
|
||||
The program will just act as-if. At test level 2 the interfaces will not be
|
||||
configured from the configuration file, the data must come from a remote
|
||||
server. At level 3 the renewal, rebind and lease time will be 60, 120
|
||||
and 180 seconds. At level 4 these times will be 60, 60, and 120. At
|
||||
level 5 these times will be 60, 60, and 60. These test levels are meant
|
||||
to debug the DHCP client code, and are best used with a high debug level.
|
||||
.TP
|
||||
.RB [ \-d [\fIlevel\fP]]
|
||||
Set the debug level (by default 1). At debug level 1 the program shows
|
||||
Ethernet and IP addresses as they are determined or configured, DHCP
|
||||
messages sent and received with little detail (one line per message), and
|
||||
memory use. At debug level 2 each DHCP packet is decoded and shown in
|
||||
detail. At debug level 3 device opens and closes are shown. The debugging
|
||||
level may also be increased by 1 at runtime by sending signal
|
||||
.BR SIGUSR1
|
||||
or turned off (set to 0) with
|
||||
.BR SIGUSR2 .
|
||||
.TP
|
||||
.BI \-f " configfile"
|
||||
Names the configuration file, by default
|
||||
.BR /etc/dhcp.conf .
|
||||
.TP
|
||||
.BI \-c " cachefile"
|
||||
Names the cache file, by default
|
||||
.BR /usr/adm/dhcp.cache .
|
||||
.TP
|
||||
.BI \-p " poolfile"
|
||||
Names the IP address pool, by default
|
||||
.BR /usr/adm/dhcp.pool .
|
||||
.SH "SEE ALSO"
|
||||
.BR RFC-2131 ,
|
||||
.BR RFC-1533 ,
|
||||
.BR dhcp.conf (5),
|
||||
.BR hosts (5),
|
||||
.BR ifconfig (8),
|
||||
.BR inet (8),
|
||||
.BR boot (8),
|
||||
.BR inetd (8),
|
||||
.BR nonamed (8).
|
||||
.SH DIAGNOSTICS
|
||||
.TP
|
||||
"'/etc/dhcp.conf', line ..."
|
||||
The program exits on any configuration file error. You have to correct the
|
||||
error and restart the program.
|
||||
.TP
|
||||
"No lease set for address ..."
|
||||
There must be a lease time defined for addresses in the pool. Correct and
|
||||
restart the program.
|
||||
.TP
|
||||
"###### declines #.#.#.# saying '...'"
|
||||
A client with the given client identifier (usually 01 followed by the client's
|
||||
Ethernet address) declines an IP address, hopefully with a message telling
|
||||
why. This usually means that the IP address is already in use by another
|
||||
host. This program, acting as a client, will tell what other host in its
|
||||
message, but Windows has no additional info alas.
|
||||
.TP
|
||||
"Got a NAK from #.#.#.# [through #.#.#.#] saying '...'"
|
||||
The server with the given IP address doesn't want us to have or keep the IP
|
||||
address we were offered or are rerequesting. This could mean that the server
|
||||
has forgotten about us and has given our address to another machine. This
|
||||
is bad if our lease hasn't yet expired. There may be a relay involved, and
|
||||
there may even be a text message with precise information.
|
||||
.TP
|
||||
"#.#.#.# offered by #.#.#.# is already in use by #:#:#:#:#:#"
|
||||
We got an ARP reply for an offered address. We won't accept it, and send
|
||||
out a DECLINE when we get an ACK.
|
||||
.TP
|
||||
"DHCP packet too big, ..."
|
||||
You've got way to much information in the configuration file, more than fits
|
||||
in a minimum size DHCP packet. (Notify the author if you really need to send
|
||||
more information. He doesn't think anyone needs to.)
|
||||
.TP
|
||||
"Pool table is corrupt"
|
||||
You will have to remove and refill the pool file. Chaos may ensue if
|
||||
there are active clients and they don't use ARP to detect each other.
|
||||
(Most do.)
|
||||
.SH BUGS
|
||||
There is no randomization of timers. Modern systems don't blink under the
|
||||
load of several clients broadcasting a few packets in sync.
|
||||
.PP
|
||||
There is no extra time spent waiting for an ARP reply. It is assumed that
|
||||
any IP stack will immediately respond, so that the DHCP server can't
|
||||
possibly beat it at sending out an ACK. (The DHCP server has to commit the
|
||||
lease to stable storage first anyway.)
|
||||
.PP
|
||||
Way more nonsense can be sent in a DHCP packet that MINIX 3 could do
|
||||
something with, but nobody does so we don't bother.
|
||||
.PP
|
||||
DHCP was invented by a rabid gerbil on speed.
|
||||
.SH AUTHOR
|
||||
Kees J. Bot <kjb@cs.vu.nl>
|
File diff suppressed because it is too large
Load Diff
@ -1,162 +0,0 @@
|
||||
/* dhcpd.h - Dynamic Host Configuration Protocol daemon.
|
||||
* Author: Kees J. Bot
|
||||
* 16 Dec 2000
|
||||
*/
|
||||
|
||||
#define nil ((void*)0)
|
||||
|
||||
#include <minix/paths.h>
|
||||
#include <net/if_ether.h>
|
||||
|
||||
/* Paths to files. */
|
||||
#define PATH_DHCPCONF _PATH_DHCPCONF
|
||||
#define PATH_DHCPPID _PATH_DHCPPID
|
||||
#define PATH_DHCPCACHE _PATH_DHCPCACHE
|
||||
#define PATH_DHCPPOOL _PATH_DHCPPOOL
|
||||
|
||||
#define CLID_MAX 32 /* Maximum client ID length. */
|
||||
|
||||
#ifndef EXTERN
|
||||
#define EXTERN extern
|
||||
#endif
|
||||
|
||||
extern int lwip;
|
||||
|
||||
EXTERN char *program; /* This program's name. */
|
||||
extern char *configfile; /* Configuration file. */
|
||||
extern char *poolfile; /* Dynamic address pool. */
|
||||
EXTERN int serving; /* True if being a DHCP server. */
|
||||
EXTERN unsigned test; /* Test level. */
|
||||
EXTERN unsigned debug; /* Debug level. */
|
||||
EXTERN asynchio_t asyn; /* Bookkeeping for all async I/O. */
|
||||
|
||||
/* BOOTP UDP ports: (That they are different is quite stupid.) */
|
||||
EXTERN u16_t port_server; /* Port server listens on. */
|
||||
EXTERN u16_t port_client; /* Port client listens on. */
|
||||
|
||||
#define arraysize(a) (sizeof(a) / sizeof((a)[0]))
|
||||
#define arraylimit(a) ((a) + arraysize(a))
|
||||
#define between(a,c,z) (sizeof(c) <= sizeof(unsigned) ? \
|
||||
(unsigned) (c) - (a) <= (unsigned) (z) - (a) : \
|
||||
(unsigned long) (c) - (a) <= (unsigned long) (z) - (a))
|
||||
|
||||
/* To treat objects as octet arrays: */
|
||||
#define B(a) ((u8_t *) (a))
|
||||
|
||||
/* Times. */
|
||||
EXTERN time_t start, now; /* Start and current time. */
|
||||
EXTERN time_t event; /* Time of the next timed event. */
|
||||
|
||||
/* Special times and periods: */
|
||||
#define NEVER (sizeof(time_t) <= sizeof(int) ? INT_MAX : LONG_MAX)
|
||||
#define DELTA_FIRST 4 /* Between first and second query. */
|
||||
#define DELTA_FAST 64 /* Unbound queries this often. */
|
||||
#define DELTA_SLOW 512 /* Bound queries are more relaxed. */
|
||||
#define N_SOLICITS 3 /* Number of solicitations. */
|
||||
#define DELTA_SOL 3 /* Time between solicitations. */
|
||||
#define DELTA_ADV 2048 /* Router adverts to self lifetime. */
|
||||
|
||||
/* Buffers for packets. */
|
||||
typedef struct buf {
|
||||
eth_hdr_t *eth; /* Ethernet header in payload. */
|
||||
ip_hdr_t *ip; /* IP header in payload. */
|
||||
udp_hdr_t *udp; /* UDP header in payload. */
|
||||
udp_io_hdr_t *udpio; /* UDP I/O header in payload. */
|
||||
dhcp_t *dhcp; /* DHCP data in payload. */
|
||||
u8_t pad[2]; /* buf[] must start at 2 mod 4. */
|
||||
/* Payload: */
|
||||
u8_t buf[ETH_MAX_PACK_SIZE];
|
||||
} buf_t;
|
||||
|
||||
#define BUF_ETH_SIZE (ETH_MAX_PACK_SIZE)
|
||||
#define BUF_IP_SIZE (BUF_ETH_SIZE - sizeof(eth_hdr_t))
|
||||
#define BUF_UDP_SIZE (BUF_IP_SIZE - sizeof(ip_hdr_t) - sizeof(udp_hdr_t) \
|
||||
+ sizeof(udp_io_hdr_t))
|
||||
|
||||
/* Type of network device open: Ethernet, ICMP, BOOTP client, BOOTP server. */
|
||||
typedef enum { FT_CLOSED, FT_ETHERNET, FT_ICMP, FT_BOOTPC, FT_BOOTPS } fdtype_t;
|
||||
|
||||
#define FT_ALL FT_CLOSED /* To close all open descriptors at once. */
|
||||
|
||||
typedef struct fd { /* An open descriptor. */
|
||||
i8_t fd; /* Open descriptor. */
|
||||
u8_t fdtype; /* Type of network open. */
|
||||
char device[sizeof("/dev/eth###")]; /* Device name. */
|
||||
u8_t n; /* Network that owns it. */
|
||||
buf_t *bp; /* Associated packet buffer. */
|
||||
time_t since; /* Open since when? */
|
||||
} fd_t;
|
||||
|
||||
/* Network state: Any IP device, Ethernet in sink mode, True Ethernet. */
|
||||
typedef enum { NT_IP, NT_SINK, NT_ETHERNET } nettype_t;
|
||||
|
||||
typedef struct network { /* Information on a network. */
|
||||
u8_t n; /* Network number. */
|
||||
ether_addr_t eth; /* Ethernet address of this net. */
|
||||
u8_t type; /* What kind of net is this? */
|
||||
i8_t sol_ct; /* Router solicitation count. */
|
||||
ether_addr_t conflict; /* Address conflict with this one. */
|
||||
unsigned flags; /* Various flags. */
|
||||
fd_t *fdp; /* Current open device. */
|
||||
struct network *wait; /* Wait for a resource list. */
|
||||
ipaddr_t ip; /* IP address of this net. */
|
||||
ipaddr_t mask; /* Associated netmask. */
|
||||
ipaddr_t gateway; /* My router. */
|
||||
ipaddr_t server; /* My DHCP server. */
|
||||
const char *hostname; /* Optional hostname to query for. */
|
||||
time_t start; /* Query or lease start time. */
|
||||
time_t delta; /* Query again after delta seconds. */
|
||||
time_t renew; /* Next query or go into renewal. */
|
||||
time_t rebind; /* When to go into rebind. */
|
||||
time_t lease; /* When our lease expires. */
|
||||
time_t solicit; /* Time to do a router solicitation. */
|
||||
} network_t;
|
||||
|
||||
/* Flags. */
|
||||
#define NF_NEGOTIATING 0x001 /* Negotiating with a DHCP server. */
|
||||
#define NF_BOUND 0x002 /* Address configured through DHCP. */
|
||||
#define NF_SERVING 0x004 /* I'm a server on this network. */
|
||||
#define NF_RELAYING 0x008 /* I'm relaying for this network. */
|
||||
#define NF_WAIT 0x010 /* Wait for a resource to free up. */
|
||||
#define NF_IRDP 0x020 /* IRDP is used on this net. */
|
||||
#define NF_CONFLICT 0x040 /* There is an address conflict. */
|
||||
#define NF_POSSESSIVE 0x080 /* Keep address if lease expires. */
|
||||
#define NF_INFORM 0x100 /* It's ok to answer DHCPINFORM. */
|
||||
|
||||
/* Functions defined in dhcpd.c. */
|
||||
void report(const char *label);
|
||||
void fatal(const char *label);
|
||||
void *allocate(size_t size);
|
||||
int ifname2if(const char *name);
|
||||
network_t *if2net(int n);
|
||||
|
||||
/* Devices.c */
|
||||
void get_buf(buf_t **bp);
|
||||
void put_buf(buf_t **bp);
|
||||
void give_buf(buf_t **dbp, buf_t **sbp);
|
||||
network_t *newnetwork(void);
|
||||
void closefd(fd_t *fdp);
|
||||
int opendev(network_t *np, fdtype_t fdtype, int compete);
|
||||
void closedev(network_t *np, fdtype_t fdtype);
|
||||
char *ipdev(int n);
|
||||
void set_ipconf(char *device, ipaddr_t ip, ipaddr_t mask, unsigned mtu);
|
||||
|
||||
/* Ether.c */
|
||||
void udp2ether(buf_t *bp, network_t *np);
|
||||
int ether2udp(buf_t *bp);
|
||||
void make_arp(buf_t *bp, network_t *np);
|
||||
int is_arp_me(buf_t *bp, network_t *np);
|
||||
void icmp_solicit(buf_t *bp);
|
||||
void icmp_advert(buf_t *bp, network_t *np);
|
||||
ipaddr_t icmp_is_advert(buf_t *bp);
|
||||
|
||||
/* Tags.c */
|
||||
#define gettag(dp, st, pd, pl) dhcp_gettag((dp), (st), (pd), (pl))
|
||||
void settag(dhcp_t *dp, int tag, void *data, size_t len);
|
||||
char *cidr_ntoa(ipaddr_t addr, ipaddr_t mask);
|
||||
void ether2clid(u8_t *clid, ether_addr_t *eth);
|
||||
void initdhcpconf(void);
|
||||
int makedhcp(dhcp_t *dp, u8_t *class, size_t calen, u8_t *client, size_t cilen,
|
||||
ipaddr_t ip, ipaddr_t ifip, network_t *np);
|
||||
char *dhcptypename(int type);
|
||||
void printdhcp(dhcp_t *dp);
|
@ -1,206 +0,0 @@
|
||||
/* ether.c - Raw Ethernet stuff
|
||||
* Author: Kees J. Bot
|
||||
* 16 Dec 2000
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <sys/asynchio.h>
|
||||
#include <net/hton.h>
|
||||
#include <net/gen/in.h>
|
||||
#include <net/gen/ether.h>
|
||||
#include <net/gen/eth_hdr.h>
|
||||
#include <net/gen/ip_hdr.h>
|
||||
#include <net/gen/icmp.h>
|
||||
#include <net/gen/icmp_hdr.h>
|
||||
#include <net/gen/oneCsum.h>
|
||||
#include <net/gen/udp.h>
|
||||
#include <net/gen/udp_hdr.h>
|
||||
#include <net/gen/dhcp.h>
|
||||
#include "arp.h"
|
||||
#include "dhcpd.h"
|
||||
|
||||
static ether_addr_t BCAST_ETH = {{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }};
|
||||
#define BCAST_IP htonl(0xFFFFFFFFUL)
|
||||
#define LOCALHOST htonl(0x7F000001UL)
|
||||
|
||||
static u16_t udp_cksum(ipaddr_t src, ipaddr_t dst, udp_hdr_t *udp)
|
||||
{
|
||||
/* Compute the checksum of an UDP packet plus data. */
|
||||
struct udp_pseudo {
|
||||
ipaddr_t src, dst;
|
||||
u8_t zero, proto;
|
||||
u16_t length;
|
||||
} pseudo;
|
||||
size_t len;
|
||||
|
||||
/* Fill in the UDP pseudo header that must be prefixed to the UDP
|
||||
* packet to compute the checksum of the whole thing.
|
||||
*/
|
||||
pseudo.src= src;
|
||||
pseudo.dst= dst;
|
||||
pseudo.zero= 0;
|
||||
pseudo.proto= IPPROTO_UDP;
|
||||
pseudo.length= udp->uh_length;
|
||||
|
||||
len= ntohs(udp->uh_length);
|
||||
if (len & 1) {
|
||||
/* Length is odd? Pad with a zero. */
|
||||
B(udp)[len++]= 0;
|
||||
}
|
||||
return oneC_sum(oneC_sum(0, &pseudo, sizeof(pseudo)), udp, len);
|
||||
}
|
||||
|
||||
void udp2ether(buf_t *bp, network_t *np)
|
||||
{
|
||||
/* Transform a packet in UDP format to raw Ethernet. Ignore the UDP
|
||||
* addresses, always broadcast from 0.0.0.0.
|
||||
*/
|
||||
udp_io_hdr_t udpio;
|
||||
|
||||
/* Save the UDP I/O header. */
|
||||
udpio= *bp->udpio;
|
||||
|
||||
/* Fill in the Ethernet, IP and UDP headers. */
|
||||
bp->eth->eh_dst= BCAST_ETH;
|
||||
bp->eth->eh_src= np->eth;
|
||||
bp->eth->eh_proto= htons(ETH_IP_PROTO);
|
||||
bp->ip->ih_vers_ihl= 0x45;
|
||||
bp->ip->ih_tos= 0;
|
||||
bp->ip->ih_length= htons(sizeof(ip_hdr_t)
|
||||
+ sizeof(udp_hdr_t) + udpio.uih_data_len);
|
||||
bp->ip->ih_id= 0;
|
||||
bp->ip->ih_flags_fragoff= ntohs(0x4000);
|
||||
bp->ip->ih_ttl= IP_MAX_TTL;
|
||||
bp->ip->ih_proto= IPPROTO_UDP;
|
||||
bp->ip->ih_hdr_chk= 0;
|
||||
bp->ip->ih_src= 0;
|
||||
bp->ip->ih_dst= BCAST_IP;
|
||||
bp->ip->ih_hdr_chk= ~oneC_sum(0, bp->ip, sizeof(*bp->ip));
|
||||
bp->udp->uh_src_port= udpio.uih_src_port;
|
||||
bp->udp->uh_dst_port= udpio.uih_dst_port;
|
||||
bp->udp->uh_length= htons(sizeof(udp_hdr_t) + udpio.uih_data_len);
|
||||
bp->udp->uh_chksum= 0;
|
||||
bp->udp->uh_chksum= ~udp_cksum(bp->ip->ih_src, bp->ip->ih_dst, bp->udp);
|
||||
}
|
||||
|
||||
int ether2udp(buf_t *bp)
|
||||
{
|
||||
/* Transform an UDP packet read from raw Ethernet to normal UDP.
|
||||
* Return true iff the packet is indeed UDP and has no errors.
|
||||
*/
|
||||
udp_io_hdr_t udpio;
|
||||
|
||||
if (bp->eth->eh_proto != htons(ETH_IP_PROTO)
|
||||
|| bp->ip->ih_vers_ihl != 0x45
|
||||
|| bp->ip->ih_proto != IPPROTO_UDP
|
||||
|| oneC_sum(0, bp->ip, 20) != (u16_t) ~0
|
||||
|| udp_cksum(bp->ip->ih_src, bp->ip->ih_dst, bp->udp) != (u16_t) ~0
|
||||
) {
|
||||
/* Not UDP/IP or checksums bad. */
|
||||
return 0;
|
||||
}
|
||||
udpio.uih_src_addr= bp->ip->ih_src;
|
||||
udpio.uih_dst_addr= bp->ip->ih_dst;
|
||||
udpio.uih_src_port= bp->udp->uh_src_port;
|
||||
udpio.uih_dst_port= bp->udp->uh_dst_port;
|
||||
udpio.uih_ip_opt_len= 0;
|
||||
udpio.uih_data_len= ntohs(bp->udp->uh_length) - sizeof(udp_hdr_t);
|
||||
*bp->udpio= udpio;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void make_arp(buf_t *bp, network_t *np)
|
||||
{
|
||||
/* Create an ARP packet to query for my IP address. */
|
||||
arp46_t *arp= (arp46_t *) bp->eth;
|
||||
|
||||
memset(arp, 0, sizeof(*arp));
|
||||
arp->dstaddr= BCAST_ETH;
|
||||
arp->srcaddr= np->eth;
|
||||
arp->ethtype= htons(ETH_ARP_PROTO);
|
||||
arp->hdr= htons(ARP_ETHERNET);
|
||||
arp->pro= htons(ETH_IP_PROTO);
|
||||
arp->op= htons(ARP_REQUEST);
|
||||
arp->hln= 6;
|
||||
arp->pln= 4;
|
||||
|
||||
arp->sha= np->eth;
|
||||
memcpy(arp->spa, &np->ip, sizeof(np->ip));
|
||||
memcpy(arp->tpa, &np->ip, sizeof(np->ip));
|
||||
}
|
||||
|
||||
int is_arp_me(buf_t *bp, network_t *np)
|
||||
{
|
||||
/* True iff an ARP packet is a reply from someone else with an address I
|
||||
* thought was mine. (That's like, bad.)
|
||||
*/
|
||||
arp46_t *arp= (arp46_t *) bp->eth;
|
||||
|
||||
if (arp->ethtype == htons(ETH_ARP_PROTO)
|
||||
&& arp->hdr == htons(ARP_ETHERNET)
|
||||
&& arp->pro == htons(ETH_IP_PROTO)
|
||||
&& arp->op == htons(ARP_REPLY)
|
||||
&& memcmp(&arp->spa, &np->ip, sizeof(np->ip)) == 0
|
||||
&& memcmp(&arp->sha, &np->eth, sizeof(np->eth)) != 0
|
||||
) {
|
||||
np->conflict= arp->sha;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void icmp_solicit(buf_t *bp)
|
||||
{
|
||||
/* Fill in a router solicitation ICMP packet. */
|
||||
icmp_hdr_t *icmp= (icmp_hdr_t *) (bp->ip + 1);
|
||||
|
||||
bp->ip->ih_vers_ihl= 0x45;
|
||||
bp->ip->ih_dst= BCAST_IP;
|
||||
|
||||
icmp->ih_type= ICMP_TYPE_ROUTE_SOL;
|
||||
icmp->ih_code= 0;
|
||||
icmp->ih_hun.ihh_unused= 0;
|
||||
icmp->ih_chksum= 0;
|
||||
icmp->ih_chksum= ~oneC_sum(0, icmp, 8);
|
||||
}
|
||||
|
||||
void icmp_advert(buf_t *bp, network_t *np)
|
||||
{
|
||||
/* Fill in a router advert to be sent to my own interface. */
|
||||
u32_t *data;
|
||||
icmp_hdr_t *icmp= (icmp_hdr_t *) (bp->ip + 1);
|
||||
|
||||
bp->ip->ih_vers_ihl= 0x45;
|
||||
bp->ip->ih_dst= LOCALHOST;
|
||||
|
||||
icmp->ih_type= ICMP_TYPE_ROUTER_ADVER;
|
||||
icmp->ih_code= 0;
|
||||
icmp->ih_hun.ihh_ram.iram_na= 1;
|
||||
icmp->ih_hun.ihh_ram.iram_aes= 2;
|
||||
icmp->ih_hun.ihh_ram.iram_lt= htons(DELTA_ADV);
|
||||
data = (u32_t *) icmp->ih_dun.uhd_data;
|
||||
data[0] = np->gateway;
|
||||
data[1] = htonl((u32_t) -9999);
|
||||
icmp->ih_chksum= 0;
|
||||
icmp->ih_chksum= ~oneC_sum(0, icmp, 16);
|
||||
}
|
||||
|
||||
ipaddr_t icmp_is_advert(buf_t *bp)
|
||||
{
|
||||
/* Check if an IP packet is a router advertisement, and if it's genuine,
|
||||
* i.e. the sender is mentioned in the packet.
|
||||
*/
|
||||
icmp_hdr_t *icmp= (icmp_hdr_t *) (bp->ip + 1);
|
||||
int i;
|
||||
|
||||
if (icmp->ih_type == ICMP_TYPE_ROUTER_ADVER) {
|
||||
for (i= 0; i < icmp->ih_hun.ihh_ram.iram_na; i++) {
|
||||
if (((u32_t *) icmp->ih_dun.uhd_data)[2*i] == bp->ip->ih_src) {
|
||||
/* It's a router! */
|
||||
return bp->ip->ih_src;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -1,926 +0,0 @@
|
||||
/* tags.c - Obtain DHCP tags from the config file
|
||||
* Author: Kees J. Bot
|
||||
* 16 Dec 2000
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <limits.h>
|
||||
#include <configfile.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/asynchio.h>
|
||||
#include <net/hton.h>
|
||||
#include <net/gen/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <net/gen/in.h>
|
||||
#include <net/gen/inet.h>
|
||||
#include <net/gen/ether.h>
|
||||
#include <net/gen/if_ether.h>
|
||||
#include <net/gen/eth_hdr.h>
|
||||
#include <net/gen/ip_hdr.h>
|
||||
#include <net/gen/udp.h>
|
||||
#include <net/gen/udp_hdr.h>
|
||||
#include <net/gen/dhcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "dhcpd.h"
|
||||
|
||||
#define doff(field) offsetof(dhcp_t, field)
|
||||
|
||||
void settag(dhcp_t *dp, int tag, void *data, size_t len)
|
||||
{
|
||||
if (!dhcp_settag(dp, tag, data, len)) {
|
||||
/* Oops, it didn't fit? Is this really Minix??? */
|
||||
fprintf(stderr,
|
||||
"%s: DHCP packet too big, please trim the configuration\n",
|
||||
program);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static int name2ip(ipaddr_t *pip, const char *name, ipaddr_t ifip)
|
||||
{
|
||||
/* Translate a name to an IP address, preferably from the hosts file,
|
||||
* but also from the DNS if being a server. Prefer the address closest
|
||||
* to the interface with IP address 'ifip' if there are choices..
|
||||
*/
|
||||
extern struct hostent *_gethostent(void); /* File reading versions. */
|
||||
extern void _endhostent(void);
|
||||
struct hostent *he;
|
||||
size_t len= strlen(name);
|
||||
u32_t d, distance= -1;
|
||||
ipaddr_t ip;
|
||||
int i;
|
||||
char *hn;
|
||||
|
||||
/* Already an IP address? */
|
||||
if (inet_aton(name, (struct in_addr *)pip)) return 1;
|
||||
|
||||
/* In the hosts file? */
|
||||
while ((he= _gethostent()) != nil) {
|
||||
hn= he->h_name;
|
||||
i= -1;
|
||||
do {
|
||||
if (strncasecmp(name, hn, len) == 0
|
||||
&& (hn[len] == 0 || hn[len] == '.')
|
||||
) {
|
||||
memcpy(&ip, he->h_addr, sizeof(ip));
|
||||
d= ntohl(ip) ^ ntohl(ifip);
|
||||
if (d < distance) {
|
||||
*pip= ip;
|
||||
distance= d;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} while ((hn= he->h_aliases[++i]) != nil);
|
||||
}
|
||||
_endhostent();
|
||||
if (distance < -1) return 1;
|
||||
|
||||
/* Nothing? Try the real DNS if being a server. */
|
||||
if (serving) {
|
||||
if ((he= gethostbyname(name)) != nil && he->h_addrtype == AF_INET) {
|
||||
/* Select the address closest to 'ifip'. */
|
||||
for (i= 0; he->h_addr_list[i] != nil; i++) {
|
||||
memcpy(&ip, he->h_addr_list[i], sizeof(ip));
|
||||
d= ntohl(ip) ^ ntohl(ifip);
|
||||
if (d < distance) {
|
||||
*pip= ip;
|
||||
distance= d;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *ip2name(ipaddr_t ip)
|
||||
{
|
||||
/* Translate an IP address to a name, etc, etc. */
|
||||
extern struct hostent *_gethostent(void); /* File reading versions. */
|
||||
extern void _endhostent(void);
|
||||
struct hostent *he;
|
||||
|
||||
/* In the hosts file? */
|
||||
while ((he= _gethostent()) != nil) {
|
||||
if (memcmp(he->h_addr, &ip, sizeof(ip)) == 0) break;
|
||||
}
|
||||
_endhostent();
|
||||
|
||||
/* Nothing? Try the real DNS if being a server. */
|
||||
if (he == nil && serving) {
|
||||
he= gethostbyaddr((char *) &ip, sizeof(ip), AF_INET);
|
||||
}
|
||||
return he != nil ? he->h_name : nil;
|
||||
}
|
||||
|
||||
static int cidr_aton(const char *cidr, ipaddr_t *addr, ipaddr_t *mask)
|
||||
{
|
||||
char *slash, *check;
|
||||
ipaddr_t a;
|
||||
int ok;
|
||||
unsigned long len;
|
||||
|
||||
if ((slash= strchr(cidr, '/')) == nil) return 0;
|
||||
|
||||
*slash++= 0;
|
||||
ok= inet_aton(cidr, (struct in_addr *)&a);
|
||||
|
||||
len= strtoul(slash, &check, 10);
|
||||
if (check == slash || *check != 0 || len > 32) ok= 0;
|
||||
|
||||
*--slash= '/';
|
||||
if (!ok) return 0;
|
||||
*addr= a;
|
||||
*mask= htonl(len == 0 ? 0 : (0xFFFFFFFFUL << (32-len)) & 0xFFFFFFFFUL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *cidr_ntoa(ipaddr_t addr, ipaddr_t mask)
|
||||
{
|
||||
ipaddr_t testmask= 0xFFFFFFFFUL;
|
||||
int n;
|
||||
static char result[sizeof("255.255.255.255/255.255.255.255")];
|
||||
|
||||
for (n= 32; n >= 0; n--) {
|
||||
if (mask == htonl(testmask)) break;
|
||||
testmask= (testmask << 1) & 0xFFFFFFFFUL;
|
||||
}
|
||||
|
||||
sprintf(result, "%s/%-2d", inet_ntoa(*(struct in_addr *)&addr), n);
|
||||
if (n == -1) strcpy(strchr(result, '/')+1,
|
||||
inet_ntoa(*(struct in_addr *)&mask));
|
||||
return result;
|
||||
}
|
||||
|
||||
static size_t ascii2octet(u8_t *b, size_t size, const char *a)
|
||||
{
|
||||
/* Convert a series of hex digit pairs to an octet (binary) array at
|
||||
* 'b' with length 'size'. Return the number of octets in 'a' or
|
||||
* -1 on error.
|
||||
*/
|
||||
size_t len;
|
||||
int n, c;
|
||||
|
||||
len= 0;
|
||||
n= 0;
|
||||
while ((c= *a++) != 0) {
|
||||
if (between('0', c, '9')) c= (c - '0') + 0x0;
|
||||
else
|
||||
if (between('a', c, 'f')) c= (c - 'a') + 0xa;
|
||||
else
|
||||
if (between('A', c, 'F')) c= (c - 'A') + 0xA;
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
if (len < size) b[len] = c << 4;
|
||||
} else {
|
||||
if (len < size) b[len] |= c;
|
||||
len++;
|
||||
}
|
||||
n ^= 1;
|
||||
}
|
||||
return n == 0 ? len : -1;
|
||||
}
|
||||
|
||||
void ether2clid(u8_t *clid, ether_addr_t *eth)
|
||||
{
|
||||
/* Convert an Ethernet address to the default client ID form. */
|
||||
clid[0]= DHCP_HTYPE_ETH;
|
||||
memcpy(clid+1, eth, DHCP_HLEN_ETH);
|
||||
}
|
||||
|
||||
static size_t ascii2clid(u8_t *clid, const char *a)
|
||||
{
|
||||
/* Convert an ethernet address, or a series of hex digits to a client ID.
|
||||
* Return its length if ok, otherwise -1.
|
||||
*/
|
||||
size_t len;
|
||||
ether_addr_t *eth;
|
||||
|
||||
if ((eth= ether_aton(a)) != nil) {
|
||||
ether2clid(clid, eth);
|
||||
len= 1+DHCP_HLEN_ETH;
|
||||
} else {
|
||||
len= ascii2octet(clid, CLID_MAX, a);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static config_t *dhcpconf; /* In-core DHCP configuration. */
|
||||
|
||||
/* DHCP tag types. */
|
||||
typedef enum { TT_ASCII, TT_BOOLEAN, TT_IP, TT_NUMBER, TT_OCTET } tagtype_t;
|
||||
|
||||
/* DHCP/BOOTP tag definitions. */
|
||||
typedef struct tagdef {
|
||||
u8_t tag; /* Tag number. */
|
||||
u8_t type; /* Type and flags. */
|
||||
u8_t gran; /* Granularity. */
|
||||
u8_t max; /* Maximum number of arguments. */
|
||||
const char *name; /* Defined name. */
|
||||
} tagdef_t;
|
||||
|
||||
#define TF_TYPE 0x07 /* To mask out the type. */
|
||||
#define TF_STATIC 0x08 /* "Static", i.e. a struct field. */
|
||||
#define TF_RO 0x10 /* Read-only, user can't set. */
|
||||
|
||||
/* List of static DHCP fields. The tag field is misused here as an offset
|
||||
* into the DHCP structure.
|
||||
*/
|
||||
static tagdef_t statictag[] = {
|
||||
{ doff(op), TT_NUMBER|TF_STATIC|TF_RO, 1, 1, "op" },
|
||||
{ doff(htype), TT_NUMBER|TF_STATIC|TF_RO, 1, 1, "htype" },
|
||||
{ doff(hlen), TT_NUMBER|TF_STATIC|TF_RO, 1, 1, "hlen" },
|
||||
{ doff(hops), TT_NUMBER|TF_STATIC|TF_RO, 1, 1, "hops" },
|
||||
{ doff(xid), TT_NUMBER|TF_STATIC|TF_RO, 4, 1, "xid" },
|
||||
{ doff(secs), TT_NUMBER|TF_STATIC|TF_RO, 2, 1, "secs" },
|
||||
{ doff(flags), TT_NUMBER|TF_STATIC|TF_RO, 2, 1, "flags" },
|
||||
{ doff(ciaddr), TT_IP|TF_STATIC|TF_RO, 1, 1, "ciaddr" },
|
||||
{ doff(yiaddr), TT_IP|TF_STATIC|TF_RO, 1, 1, "yiaddr" },
|
||||
{ doff(siaddr), TT_IP|TF_STATIC, 1, 1, "siaddr" },
|
||||
{ doff(giaddr), TT_IP|TF_STATIC|TF_RO, 1, 1, "giaddr" },
|
||||
{ doff(chaddr), TT_OCTET|TF_STATIC|TF_RO, 1, 16, "chaddr" },
|
||||
{ doff(sname), TT_ASCII|TF_STATIC, 1, 64, "sname" },
|
||||
{ doff(file), TT_ASCII|TF_STATIC, 1, 128, "file" },
|
||||
};
|
||||
#define N_STATIC arraysize(statictag)
|
||||
|
||||
static tagdef_t alltagdef[N_STATIC + 254]; /* List of tag definitions. */
|
||||
#define tagdef (alltagdef+N_STATIC-1) /* Just the optional ones. */
|
||||
|
||||
#define tagdefined(tp) ((tp)->name != nil)
|
||||
|
||||
static void inittagdef(void)
|
||||
{
|
||||
/* Initialize the tag definitions from the "tag" commands in the config
|
||||
* file.
|
||||
*/
|
||||
int t;
|
||||
tagdef_t *tp;
|
||||
static tagdef_t predef[] = {
|
||||
{ DHCP_TAG_NETMASK, TT_IP, 1, 1, "netmask" },
|
||||
{ DHCP_TAG_GATEWAY, TT_IP, 1, 255, "gateway" },
|
||||
{ DHCP_TAG_DNS, TT_IP, 1, 255, "DNSserver" },
|
||||
};
|
||||
static char *typenames[] = { "ascii", "boolean", "ip", "number", "octet" };
|
||||
config_t *cfg;
|
||||
static u8_t rotags[] = {
|
||||
DHCP_TAG_REQIP, DHCP_TAG_OVERLOAD, DHCP_TAG_TYPE, DHCP_TAG_SERVERID,
|
||||
DHCP_TAG_REQPAR, DHCP_TAG_MESSAGE, DHCP_TAG_MAXDHCP
|
||||
};
|
||||
|
||||
for (t= 1; t <= 254; t++) {
|
||||
tp= &tagdef[t];
|
||||
tp->tag= t;
|
||||
tp->type= TT_OCTET;
|
||||
tp->name= nil;
|
||||
}
|
||||
|
||||
/* Set the static and "all Minix needs" tags. */
|
||||
memcpy(alltagdef, statictag, sizeof(statictag));
|
||||
for (tp= predef; tp < arraylimit(predef); tp++) tagdef[tp->tag] = *tp;
|
||||
|
||||
/* Search for tag definitions in the config file. */
|
||||
for (cfg= dhcpconf; cfg != nil; cfg= cfg->next) {
|
||||
config_t *cmd= cfg->list;
|
||||
|
||||
if (strcasecmp(cmd->word, "tag") == 0) {
|
||||
if (config_length(cmd) == 6
|
||||
&& (cmd->next->flags & CFG_DULONG)
|
||||
&& config_isatom(cmd->next->next)
|
||||
&& config_isatom(cmd->next->next->next)
|
||||
&& (cmd->next->next->next->next->flags & CFG_DULONG)
|
||||
&& (cmd->next->next->next->next->next->flags & CFG_DULONG)
|
||||
) {
|
||||
unsigned long tag, gran, max;
|
||||
const char *name, *typename;
|
||||
unsigned type;
|
||||
|
||||
tag= strtoul(cmd->next->word, nil, 10);
|
||||
name= cmd->next->next->word;
|
||||
typename= cmd->next->next->next->word;
|
||||
gran= strtoul(cmd->next->next->next->next->word, nil, 10);
|
||||
max= strtoul(cmd->next->next->next->next->next->word, nil, 10);
|
||||
|
||||
for (type= 0; type < arraysize(typenames); type++) {
|
||||
if (strcasecmp(typename, typenames[type]) == 0) break;
|
||||
}
|
||||
|
||||
if (!(1 <= tag && tag <= 254)
|
||||
|| !(type < arraysize(typenames))
|
||||
|| !((type == TT_NUMBER
|
||||
&& (gran == 1 || gran == 2 || gran == 4))
|
||||
|| (type != TT_NUMBER && 1 <= gran && gran <= 16))
|
||||
|| !(max <= 255)
|
||||
) {
|
||||
fprintf(stderr,
|
||||
"\"%s\", line %u: Tag definition is incorrect\n",
|
||||
cmd->file, cmd->line);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
tp= &tagdef[(int)tag];
|
||||
tp->type= type;
|
||||
tp->name= name;
|
||||
tp->gran= gran;
|
||||
tp->max= max;
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"\"%s\", line %u: Usage: tag number name type granularity max\n",
|
||||
cmd->file, cmd->line);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Many DHCP tags are not for the user to play with. */
|
||||
for (t= 0; t < arraysize(rotags); t++) tagdef[rotags[t]].type |= TF_RO;
|
||||
}
|
||||
|
||||
static tagdef_t *tagdefbyname(const char *name)
|
||||
{
|
||||
/* Find a tag definition by the name of the tag. Return null if not
|
||||
* defined.
|
||||
*/
|
||||
tagdef_t *tp;
|
||||
|
||||
for (tp= alltagdef; tp < arraylimit(alltagdef); tp++) {
|
||||
if (tagdefined(tp) && strcasecmp(tp->name, name) == 0) return tp;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
void initdhcpconf(void)
|
||||
{
|
||||
/* Read/refresh configuration from the DHCP configuration file. */
|
||||
dhcpconf= config_read(configfile, 0, dhcpconf);
|
||||
if (config_renewed(dhcpconf)) inittagdef();
|
||||
}
|
||||
|
||||
static void configtag(dhcp_t *dp, config_t *cmd, ipaddr_t ifip)
|
||||
{
|
||||
/* Add a tag to a DHCP packet from the config file. */
|
||||
tagdef_t *tp;
|
||||
u8_t data[260], *d;
|
||||
size_t i;
|
||||
int delete= 0;
|
||||
|
||||
if (strcasecmp(cmd->word, "no") == 0) {
|
||||
if (config_length(cmd) != 2 || !config_isatom(cmd->next)) {
|
||||
fprintf(stderr, "\"%s\", line %u: Usage: no tag-name\n",
|
||||
cmd->file, cmd->line);
|
||||
exit(1);
|
||||
}
|
||||
cmd= cmd->next;
|
||||
delete= 1;
|
||||
}
|
||||
|
||||
if ((tp= tagdefbyname(cmd->word)) == nil) {
|
||||
fprintf(stderr, "\"%s\", line %u: Unknown tag '%s'\n",
|
||||
cmd->file, cmd->line, cmd->word);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (tp->type & TF_RO) {
|
||||
fprintf(stderr, "\"%s\", line %u: Tag '%s' can't be configured\n",
|
||||
cmd->file, cmd->line, cmd->word);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
i= 0;
|
||||
d= data;
|
||||
if (!delete) {
|
||||
config_t *arg= cmd->next;
|
||||
do {
|
||||
switch (tp->type & TF_TYPE) {
|
||||
case TT_ASCII: {
|
||||
if (arg == nil || !config_isatom(arg) || arg->next != nil) {
|
||||
fprintf(stderr, "\"%s\", line %u: Usage: %s string\n",
|
||||
cmd->file, cmd->line, cmd->word);
|
||||
exit(1);
|
||||
}
|
||||
strncpy((char *) data, arg->word, sizeof(data));
|
||||
d += i = strnlen((char *) data, sizeof(data));
|
||||
break;}
|
||||
case TT_BOOLEAN: {
|
||||
if (arg == nil || !config_isatom(arg)
|
||||
|| !(strcasecmp(arg->word, "false") == 0
|
||||
|| strcasecmp(arg->word, "true") == 0)
|
||||
) {
|
||||
fprintf(stderr,
|
||||
"\"%s\", line %u: Usage: %s false|true ...\n",
|
||||
cmd->file, cmd->line, cmd->word);
|
||||
exit(1);
|
||||
}
|
||||
if (d < arraylimit(data)) {
|
||||
*d++ = (arg->word[0] != 'f' && arg->word[0] != 'F');
|
||||
}
|
||||
i++;
|
||||
break;}
|
||||
case TT_IP: {
|
||||
ipaddr_t ip;
|
||||
unsigned long len;
|
||||
char *end;
|
||||
|
||||
if (arg == nil || !config_isatom(arg)) {
|
||||
fprintf(stderr, "\"%s\", line %u: Usage: %s host ...\n",
|
||||
cmd->file, cmd->line, cmd->word);
|
||||
exit(1);
|
||||
}
|
||||
if (arg->word[0] == '/'
|
||||
&& between(1, len= strtoul(arg->word+1, &end, 10), 31)
|
||||
&& *end == 0
|
||||
) {
|
||||
ip= htonl((0xFFFFFFFFUL << (32-len)) & 0xFFFFFFFFUL);
|
||||
} else
|
||||
if (!name2ip(&ip, arg->word, ifip)) {
|
||||
fprintf(stderr,
|
||||
"\"%s\", line %u: Can't translate %s to an IP address\n",
|
||||
arg->file, arg->line, arg->word);
|
||||
exit(1);
|
||||
}
|
||||
if (d <= arraylimit(data) - sizeof(ip)) {
|
||||
memcpy(d, &ip, sizeof(ip));
|
||||
d += sizeof(ip);
|
||||
}
|
||||
i++;
|
||||
break;}
|
||||
case TT_NUMBER: {
|
||||
unsigned long n;
|
||||
int g;
|
||||
|
||||
if (arg == nil || !(arg->flags & CFG_CLONG)) {
|
||||
fprintf(stderr, "\"%s\", line %u: Usage: %s number ...\n",
|
||||
cmd->file, cmd->line, cmd->word);
|
||||
exit(1);
|
||||
}
|
||||
n= strtoul(arg->word, nil, 0);
|
||||
g= tp->gran;
|
||||
do {
|
||||
if (d <= arraylimit(data)) *d++ = (n >> (--g * 8)) & 0xFF;
|
||||
} while (g != 0);
|
||||
i++;
|
||||
break;}
|
||||
case TT_OCTET: {
|
||||
if (arg == nil || !config_isatom(arg) || arg->next != nil) {
|
||||
fprintf(stderr, "\"%s\", line %u: Usage: %s hexdigits\n",
|
||||
cmd->file, cmd->line, cmd->word);
|
||||
exit(1);
|
||||
}
|
||||
i= ascii2octet(data, sizeof(data), arg->word);
|
||||
if (i == -1) {
|
||||
fprintf(stderr,
|
||||
"\"%s\", line %u: %s: Bad hexdigit string\n",
|
||||
arg->file, arg->line, arg->word);
|
||||
exit(1);
|
||||
}
|
||||
d= data + i;
|
||||
break;}
|
||||
}
|
||||
} while ((arg= arg->next) != nil);
|
||||
|
||||
if (d > data + 255) {
|
||||
fprintf(stderr, "\"%s\", line %u: Tag value is way too big\n",
|
||||
cmd->file, cmd->line);
|
||||
exit(1);
|
||||
}
|
||||
if ((tp->type & TF_TYPE) != TT_NUMBER && (i % tp->gran) != 0) {
|
||||
fprintf(stderr,
|
||||
"\"%s\", line %u: Expected a multiple of %d initializers\n",
|
||||
cmd->file, cmd->line, tp->gran);
|
||||
exit(1);
|
||||
}
|
||||
if (tp->max != 0 && i > tp->max) {
|
||||
fprintf(stderr,
|
||||
"\"%s\", line %u: Got %d initializers, can have only %d\n",
|
||||
cmd->file, cmd->line, (int) i, tp->max);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (tp->type & TF_STATIC) {
|
||||
size_t len= tp->gran * tp->max;
|
||||
if ((tp->type & TF_TYPE) == TT_IP) len *= sizeof(ipaddr_t);
|
||||
memset(B(dp) + tp->tag, 0, len);
|
||||
memcpy(B(dp) + tp->tag, data, (d - data));
|
||||
} else {
|
||||
settag(dp, tp->tag, data, (d - data));
|
||||
}
|
||||
}
|
||||
|
||||
int makedhcp(dhcp_t *dp, u8_t *class, size_t calen, u8_t *client, size_t cilen,
|
||||
ipaddr_t ip, ipaddr_t ifip, network_t *np)
|
||||
{
|
||||
/* Fill in a DHCP packet at 'dp' for the host identified by the
|
||||
* (class, client, ip) combination. Makedhcp is normally called twice,
|
||||
* once to find the IP address (so ip == 0) and once again to find all
|
||||
* data that goes with that IP address (ip != 0). On the first call the
|
||||
* return value of this function should be ignored and only 'yiaddr'
|
||||
* checked and used as 'ip' on the next pass. True is returned iff there
|
||||
* is information for the client on the network at interface address
|
||||
* 'ifip', by checking if the 'ip' and 'ifip' are on the same network.
|
||||
* If np is nonnull then we are working for one of our own interfaces, so
|
||||
* options can be set and adjourning interfaces can be programmed.
|
||||
*/
|
||||
config_t *todo[16];
|
||||
size_t ntodo= 0;
|
||||
ipaddr_t hip, mask;
|
||||
u8_t *pmask;
|
||||
char *hostname;
|
||||
u32_t distance= -1;
|
||||
|
||||
initdhcpconf();
|
||||
|
||||
/* Start creating a packet. */
|
||||
dhcp_init(dp);
|
||||
dp->op= DHCP_BOOTREPLY;
|
||||
|
||||
/* The initial TODO list is the whole DHCP config. */
|
||||
todo[ntodo++]= dhcpconf;
|
||||
|
||||
while (ntodo > 0) {
|
||||
config_t *cmd, *follow;
|
||||
|
||||
if (todo[ntodo-1] == nil) { ntodo--; continue; }
|
||||
cmd= todo[ntodo-1]->list;
|
||||
todo[ntodo-1]= todo[ntodo-1]->next;
|
||||
|
||||
follow= nil; /* Macro or list to follow next? */
|
||||
|
||||
if (strcasecmp(cmd->word, "client") == 0) {
|
||||
u8_t cfgid[CLID_MAX];
|
||||
size_t cfglen;
|
||||
char *name;
|
||||
int ifno;
|
||||
u32_t d;
|
||||
|
||||
if (between(3, config_length(cmd), 5)
|
||||
&& config_isatom(cmd->next)
|
||||
&& (cfglen= ascii2clid(cfgid, cmd->next->word)) != -1
|
||||
&& config_isatom(cmd->next->next)
|
||||
&& (((ifno= ifname2if(cmd->next->next->word)) == -1
|
||||
&& config_length(cmd) <= 4)
|
||||
|| ((ifno= ifname2if(cmd->next->next->word)) != -1
|
||||
&& config_length(cmd) >= 4
|
||||
&& config_isatom(cmd->next->next->next)))
|
||||
) {
|
||||
if (cilen == cfglen && memcmp(client, cfgid, cilen) == 0
|
||||
&& (ifno == -1 || np == nil || ifno == np->n)
|
||||
) {
|
||||
config_t *atname= cmd->next->next;
|
||||
if (ifno != -1) atname= atname->next;
|
||||
name= (char *)atname->word;
|
||||
|
||||
if (name2ip(&hip, name, ifip) && (ip == 0 || ip == hip)) {
|
||||
d= ntohl(hip) ^ ntohl(ifip);
|
||||
if (d < distance) {
|
||||
dp->yiaddr= hip;
|
||||
follow= atname->next;
|
||||
distance= d;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"\"%s\", line %u: Usage: client ID [ip#] host [macro|{params}]\n",
|
||||
cmd->file, cmd->line);
|
||||
exit(1);
|
||||
}
|
||||
} else
|
||||
if (strcasecmp(cmd->word, "class") == 0) {
|
||||
config_t *clist;
|
||||
int match;
|
||||
|
||||
match= 0;
|
||||
for (clist= cmd->next; clist != nil
|
||||
&& clist->next != nil
|
||||
&& config_isatom(clist); clist= clist->next) {
|
||||
if (calen > 0
|
||||
&& strncmp(clist->word, (char *) class, calen) == 0
|
||||
) {
|
||||
match= 1;
|
||||
}
|
||||
}
|
||||
if (clist == cmd->next || clist->next != nil) {
|
||||
fprintf(stderr,
|
||||
"\"%s\", line %u: Usage: class class-name ... macro|{params}\n",
|
||||
cmd->file, cmd->line);
|
||||
}
|
||||
if (match) follow= clist;
|
||||
} else
|
||||
if (strcasecmp(cmd->word, "host") == 0) {
|
||||
if (config_length(cmd) == 3
|
||||
&& config_isatom(cmd->next)
|
||||
) {
|
||||
if (ip != 0) {
|
||||
if (cidr_aton(cmd->next->word, &hip, &mask)) {
|
||||
if (((hip ^ ip) & mask) == 0) {
|
||||
if (!gettag(dp, DHCP_TAG_NETMASK, nil, nil)) {
|
||||
settag(dp, DHCP_TAG_NETMASK,
|
||||
&mask, sizeof(mask));
|
||||
}
|
||||
dp->yiaddr= ip;
|
||||
follow= cmd->next->next;
|
||||
}
|
||||
} else
|
||||
if (name2ip(&hip, cmd->next->word, ifip)) {
|
||||
if (hip == ip) {
|
||||
dp->yiaddr= ip;
|
||||
follow= cmd->next->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"\"%s\", line %u: Usage: host host-spec macro|{params}\n",
|
||||
cmd->file, cmd->line);
|
||||
exit(1);
|
||||
}
|
||||
} else
|
||||
if (strcasecmp(cmd->word, "interface") == 0) {
|
||||
if (between(3, config_length(cmd), 4)
|
||||
&& config_isatom(cmd->next)
|
||||
&& config_isatom(cmd->next->next)
|
||||
) {
|
||||
network_t *ifnp;
|
||||
|
||||
if (np != nil) {
|
||||
if ((ifnp= if2net(ifname2if(cmd->next->word))) == nil) {
|
||||
fprintf(stderr,
|
||||
"\"%s\", line %u: Can't find interface %s\n",
|
||||
cmd->next->file, cmd->next->line, cmd->next->word);
|
||||
exit(1);
|
||||
}
|
||||
if (!name2ip(&hip, cmd->next->next->word, 0)) {
|
||||
fprintf(stderr,
|
||||
"\"%s\", line %u: Can't find IP address of %s\n",
|
||||
cmd->next->next->file, cmd->next->next->line,
|
||||
cmd->next->next->word);
|
||||
exit(1);
|
||||
}
|
||||
ifnp->ip= hip;
|
||||
if (ifnp == np) {
|
||||
dp->yiaddr= hip;
|
||||
follow= cmd->next->next->next;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"\"%s\", line %u: Usage: interface ip# host%s\n",
|
||||
cmd->file, cmd->line, ntodo==1 ? " [macro|{params}]" : "");
|
||||
exit(1);
|
||||
}
|
||||
} else
|
||||
if (strcasecmp(cmd->word, "macro") == 0) {
|
||||
if (config_length(cmd) == 2 && config_isatom(cmd->next)) {
|
||||
follow= cmd->next;
|
||||
} else
|
||||
if (ntodo > 1) {
|
||||
fprintf(stderr, "\"%s\", line %u: Usage: macro macro-name\n",
|
||||
cmd->file, cmd->line);
|
||||
exit(1);
|
||||
}
|
||||
} else
|
||||
if (strcasecmp(cmd->word, "tag") == 0) {
|
||||
if (ntodo > 1) {
|
||||
fprintf(stderr,
|
||||
"\"%s\", line %u: A %s can't be defined here\n",
|
||||
cmd->file, cmd->line, cmd->word);
|
||||
exit(1);
|
||||
}
|
||||
} else
|
||||
if (strcasecmp(cmd->word, "option") == 0) {
|
||||
int ifno;
|
||||
network_t *ifnp;
|
||||
config_t *opt;
|
||||
|
||||
if ((opt= cmd->next) != nil
|
||||
&& config_isatom(opt)
|
||||
&& (ifno= ifname2if(opt->word)) != -1
|
||||
) {
|
||||
if ((ifnp= if2net(ifno)) == nil) {
|
||||
fprintf(stderr,
|
||||
"\"%s\", line %u: Interface %s is not enabled\n",
|
||||
opt->file, opt->line, opt->word);
|
||||
exit(1);
|
||||
}
|
||||
opt= opt->next;
|
||||
} else {
|
||||
ifnp= np;
|
||||
}
|
||||
|
||||
if (between(1, config_length(opt), 2)
|
||||
&& config_isatom(opt)
|
||||
&& strcasecmp(opt->word, "server") == 0
|
||||
&& (opt->next == nil
|
||||
|| strcasecmp(opt->next->word, "inform") == 0)
|
||||
) {
|
||||
if (np != nil) {
|
||||
ifnp->flags |= NF_SERVING;
|
||||
if (opt->next != nil) ifnp->flags |= NF_INFORM;
|
||||
}
|
||||
} else
|
||||
if (config_length(opt) == 2
|
||||
&& config_isatom(opt)
|
||||
&& strcasecmp(opt->word, "relay") == 0
|
||||
&& config_isatom(opt->next)
|
||||
) {
|
||||
if (np != nil) {
|
||||
if (!name2ip(&hip, opt->next->word, ifip)) {
|
||||
fprintf(stderr,
|
||||
"\"%s\", line %u: Can't find IP address of %s\n",
|
||||
opt->next->file, opt->next->line,
|
||||
opt->next->word);
|
||||
exit(1);
|
||||
}
|
||||
ifnp->flags |= NF_RELAYING;
|
||||
ifnp->server= hip;
|
||||
}
|
||||
} else
|
||||
if (config_length(opt) == 1
|
||||
&& config_isatom(opt)
|
||||
&& strcasecmp(opt->word, "possessive") == 0
|
||||
) {
|
||||
if (np != nil) ifnp->flags |= NF_POSSESSIVE;
|
||||
} else
|
||||
if (config_length(opt) == 2
|
||||
&& config_isatom(opt)
|
||||
&& strcasecmp(opt->word, "hostname") == 0
|
||||
&& config_isatom(opt->next)
|
||||
) {
|
||||
if (np != nil) np->hostname= opt->next->word;
|
||||
} else {
|
||||
fprintf(stderr, "\"%s\", line %u: Unknown option\n",
|
||||
cmd->file, cmd->line);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
/* Must be an actual data carrying tag. */
|
||||
configtag(dp, cmd, ifip);
|
||||
}
|
||||
|
||||
if (follow != nil) {
|
||||
/* A client/class/host entry selects a macro or list that must
|
||||
* be followed next.
|
||||
*/
|
||||
config_t *macro;
|
||||
|
||||
if (config_isatom(follow)) { /* Macro name */
|
||||
config_t *cfg;
|
||||
|
||||
for (cfg= dhcpconf; cfg != nil; cfg= cfg->next) {
|
||||
macro= cfg->list;
|
||||
|
||||
if (strcasecmp(macro->word, "macro") == 0) {
|
||||
if (config_length(macro) == 3
|
||||
&& config_isatom(macro->next)
|
||||
&& config_issub(macro->next->next)
|
||||
) {
|
||||
if (strcasecmp(macro->next->word, follow->word) == 0
|
||||
) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"\"%s\", line %u: Usage: macro macro-name {params}\n",
|
||||
macro->file, macro->line);
|
||||
}
|
||||
}
|
||||
}
|
||||
follow= cfg == nil ? nil : macro->next->next->list;
|
||||
} else {
|
||||
/* Simply a list of more tags and stuff. */
|
||||
follow= follow->list;
|
||||
}
|
||||
|
||||
if (ntodo == arraysize(todo)) {
|
||||
fprintf(stderr, "\"%s\", line %u: Nesting is too deep\n",
|
||||
follow->file, follow->line);
|
||||
exit(1);
|
||||
}
|
||||
todo[ntodo++]= follow;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if the IP and netmask are OK for the interface. */
|
||||
if (!gettag(dp, DHCP_TAG_NETMASK, &pmask, nil)) return 0;
|
||||
memcpy(&mask, pmask, sizeof(mask));
|
||||
if (((ip ^ ifip) & mask) != 0) return 0;
|
||||
|
||||
/* Fill in the hostname and/or domain. */
|
||||
if ((hostname= ip2name(ip)) != nil) {
|
||||
char *domain;
|
||||
|
||||
if ((domain= strchr(hostname, '.')) != nil) *domain++ = 0;
|
||||
|
||||
if (!gettag(dp, DHCP_TAG_HOSTNAME, nil, nil)) {
|
||||
settag(dp, DHCP_TAG_HOSTNAME, hostname, strlen(hostname));
|
||||
}
|
||||
|
||||
if (domain != nil && !gettag(dp, DHCP_TAG_DOMAIN, nil, nil)) {
|
||||
settag(dp, DHCP_TAG_DOMAIN, domain, strlen(domain));
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char *dhcpopname(int op)
|
||||
{
|
||||
static char *onames[] = { "??\?", "REQUEST", "REPLY" };
|
||||
return onames[op < arraysize(onames) ? op : 0];
|
||||
}
|
||||
|
||||
char *dhcptypename(int type)
|
||||
{
|
||||
static char *tnames[] = {
|
||||
"??\?", "DISCOVER", "OFFER", "REQUEST", "DECLINE",
|
||||
"ACK", "NAK", "RELEASE", "INFORM"
|
||||
};
|
||||
return tnames[type < arraysize(tnames) ? type : 0];
|
||||
}
|
||||
|
||||
void printdhcp(dhcp_t *dp)
|
||||
{
|
||||
/* Print the contents of a DHCP packet, usually for debug purposes. */
|
||||
tagdef_t *tp;
|
||||
u8_t *data, *ovld;
|
||||
size_t i, len;
|
||||
|
||||
for (tp= alltagdef; tp < arraylimit(alltagdef); tp++) {
|
||||
if (tp->type & TF_STATIC) {
|
||||
data= B(dp) + tp->tag;
|
||||
len= tp->gran * tp->max;
|
||||
if ((tp->type & TF_TYPE) == TT_IP) len *= sizeof(ipaddr_t);
|
||||
if (tp->tag == doff(chaddr)) len= dp->hlen;
|
||||
|
||||
/* Don't show uninteresting stuff. */
|
||||
if (tp->tag == doff(htype) && dp->htype == DHCP_HTYPE_ETH) continue;
|
||||
|
||||
if (tp->tag == doff(hlen) && dp->hlen == DHCP_HLEN_ETH) continue;
|
||||
|
||||
if ((tp->tag == doff(file) || tp->tag == doff(sname))
|
||||
&& gettag(dp, DHCP_TAG_OVERLOAD, &ovld, nil)
|
||||
&& (ovld[0] & (tp->tag == doff(file) ? 1 : 2))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
for (i= 0; i < len && data[i] == 0; i++) {}
|
||||
if (i == len) continue;
|
||||
} else {
|
||||
if (!gettag(dp, tp->tag, &data, &len)) continue;
|
||||
}
|
||||
|
||||
if (tagdefined(tp)) {
|
||||
printf("\t%s =", tp->name);
|
||||
} else {
|
||||
printf("\tT%d =", tp->tag);
|
||||
}
|
||||
|
||||
i= 0;
|
||||
while (i < len) {
|
||||
switch (tp->type & TF_TYPE) {
|
||||
case TT_ASCII: {
|
||||
printf(" \"%.*s\"", (int) len, data);
|
||||
i= len;
|
||||
break;}
|
||||
case TT_BOOLEAN: {
|
||||
printf(data[i++] == 0 ? " false" : " true");
|
||||
break;}
|
||||
case TT_IP: {
|
||||
ipaddr_t ip;
|
||||
memcpy(&ip, data+i, sizeof(ip));
|
||||
printf(" %s", inet_ntoa(*(struct in_addr *)&ip));
|
||||
i += sizeof(ip);
|
||||
break;}
|
||||
case TT_NUMBER: {
|
||||
u32_t n= 0;
|
||||
int g= tp->gran;
|
||||
|
||||
do n= (n << 8) | data[i++]; while (--g != 0);
|
||||
printf(" %lu", (unsigned long) n);
|
||||
if ((tp->type & TF_STATIC) && tp->tag == doff(op)) {
|
||||
printf(" (%s)", dhcpopname(n));
|
||||
}
|
||||
if (!(tp->type & TF_STATIC) && tp->tag == DHCP_TAG_TYPE) {
|
||||
printf(" (%s)", dhcptypename(n));
|
||||
}
|
||||
break;}
|
||||
case TT_OCTET: {
|
||||
if (i == 0) fputc(' ', stdout);
|
||||
printf("%02X", data[i++]);
|
||||
break;}
|
||||
}
|
||||
}
|
||||
fputc('\n', stdout);
|
||||
}
|
||||
}
|
@ -8,11 +8,6 @@
|
||||
#define _PATH_SYSTEM_CONF_DIR "/etc/system.conf.d"
|
||||
#define _PATH_SYSTEM_CONF_PKG_DIR "/usr/pkg/etc/system.conf.d"
|
||||
|
||||
#define _PATH_DHCPCONF "/etc/dhcp.conf"
|
||||
#define _PATH_DHCPPID "/usr/run/dhcpd.pid"
|
||||
#define _PATH_DHCPCACHE "/usr/adm/dhcp.cache"
|
||||
#define _PATH_DHCPPOOL "/usr/adm/dhcp.pool"
|
||||
|
||||
#define _PATH_RAMDISK "/dev/ram"
|
||||
|
||||
#define _PATH_DRIVERS "/service"
|
||||
|
@ -1,55 +0,0 @@
|
||||
/* dhcp_gettag() Author: Kees J. Bot
|
||||
* 1 Dec 2000
|
||||
*/
|
||||
#define nil ((void*)0)
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <net/hton.h>
|
||||
#include <net/gen/in.h>
|
||||
#include <net/gen/dhcp.h>
|
||||
|
||||
#define arraysize(a) (sizeof(a) / sizeof((a)[0]))
|
||||
|
||||
int dhcp_gettag(dhcp_t *dp, int searchtag, u8_t **pdata, size_t *plen)
|
||||
{
|
||||
/* Find a tag in the options field, or possibly in the file or sname
|
||||
* fields. Return true iff found, and return the data and/or length if
|
||||
* their pointers are non-null.
|
||||
*/
|
||||
u8_t *p;
|
||||
u8_t *optfield[3];
|
||||
size_t optlen[3];
|
||||
int i, tag, len;
|
||||
|
||||
/* The DHCP magic number must be correct, or no tags. */
|
||||
if (dp->magic != DHCP_MAGIC) return 0;
|
||||
|
||||
optfield[0]= dp->options;
|
||||
optlen[0]= arraysize(dp->options);
|
||||
optfield[1]= dp->file;
|
||||
optlen[1]= 0; /* Unknown if used for options yet. */
|
||||
optfield[2]= dp->sname;
|
||||
optlen[2]= 0;
|
||||
|
||||
for (i= 0; i < 3; i++) {
|
||||
p= optfield[i];
|
||||
while (p < optfield[i] + optlen[i]) {
|
||||
tag= *p++;
|
||||
if (tag == 255) break;
|
||||
len= tag == 0 ? 0 : *p++;
|
||||
if (tag == searchtag) {
|
||||
if (pdata != nil) *pdata= p;
|
||||
if (plen != nil) *plen= len;
|
||||
return 1;
|
||||
}
|
||||
if (tag == DHCP_TAG_OVERLOAD) {
|
||||
/* There are also options in the file or sname field. */
|
||||
if (*p & 1) optlen[1]= arraysize(dp->file);
|
||||
if (*p & 2) optlen[1]= arraysize(dp->sname);
|
||||
}
|
||||
p += len;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
/* dhcp_init(), dhcp_settag() Author: Kees J. Bot
|
||||
* 1 Dec 2000
|
||||
*/
|
||||
#define nil ((void*)0)
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <net/hton.h>
|
||||
#include <net/gen/in.h>
|
||||
#include <net/gen/dhcp.h>
|
||||
|
||||
#define arraysize(a) (sizeof(a) / sizeof((a)[0]))
|
||||
#define arraylimit(a) ((a) + arraysize(a))
|
||||
|
||||
void dhcp_init(dhcp_t *dp)
|
||||
{
|
||||
/* Initialize a DHCP packet. */
|
||||
memset(dp, 0, offsetof(dhcp_t, magic));
|
||||
dp->magic= DHCP_MAGIC;
|
||||
memset(dp->options, 255, sizeof(dp->options));
|
||||
}
|
||||
|
||||
int dhcp_settag(dhcp_t *dp, int tag, void *data, size_t len)
|
||||
{
|
||||
/* Add a tag to a DHCP packet. No padding. Only do the options field.
|
||||
* (This is Minix, we don't need megabytes of silly bits of data.)
|
||||
* The length may be zero to delete a tag.
|
||||
*/
|
||||
u8_t *p;
|
||||
int n;
|
||||
|
||||
if (tag <= 0 || tag >= 255) return 0;
|
||||
|
||||
for (p= dp->options; p < arraylimit(dp->options) && *p != 255; p += n) {
|
||||
n= 1 + 1 + p[1];
|
||||
if (*p == tag) {
|
||||
/* The tag is already there, remove it so it gets replaced. */
|
||||
memmove(p, p + n, arraylimit(dp->options) - (p + n));
|
||||
memset(arraylimit(dp->options) - n, 255, n);
|
||||
n= 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add tag. */
|
||||
if (len == 0) {
|
||||
/* We're merely deleting a tag. */
|
||||
} else
|
||||
if (p + 1 + 1 + len <= arraylimit(dp->options)) {
|
||||
*p++ = tag;
|
||||
*p++ = len;
|
||||
memcpy(p, data, len);
|
||||
} else {
|
||||
/* Oops, it didn't fit? Is this really Minix??? */
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
@ -71,9 +71,6 @@ A file that doesn't exist is seen as empty.
|
||||
.PP
|
||||
A generic configuration file can be read with the functions described in
|
||||
.BR configfile (3).
|
||||
.SH EXAMPLES
|
||||
Have a look at
|
||||
.BR /etc/dhcp.conf .
|
||||
.SH "SEE ALSO"
|
||||
.BR configfile (3).
|
||||
.SH NOTES
|
||||
|
Loading…
x
Reference in New Issue
Block a user