summaryrefslogtreecommitdiff
path: root/usr.sbin/dhcpd
diff options
context:
space:
mode:
authorYASUOKA Masahiko <yasuoka@cvs.openbsd.org>2014-07-11 09:42:28 +0000
committerYASUOKA Masahiko <yasuoka@cvs.openbsd.org>2014-07-11 09:42:28 +0000
commitf070257addf0e815b57d7380d6ae2b71fdb65679 (patch)
tree2851b64f28c2381a9bdf4eb341d6b1d25fc6b6f2 /usr.sbin/dhcpd
parent6f99326d0e5b7d2b86664a411cbc72ead1ab7930 (diff)
Add -u option to bind UDP port as a socket to answer DHCPINFORM from
the clients on non ethernet interfaces (eg. tun(4) or pppx(4)). input krw ok krw
Diffstat (limited to 'usr.sbin/dhcpd')
-rw-r--r--usr.sbin/dhcpd/Makefile5
-rw-r--r--usr.sbin/dhcpd/bootp.c6
-rw-r--r--usr.sbin/dhcpd/bpf.c6
-rw-r--r--usr.sbin/dhcpd/dhcp.c10
-rw-r--r--usr.sbin/dhcpd/dhcpd.827
-rw-r--r--usr.sbin/dhcpd/dhcpd.c28
-rw-r--r--usr.sbin/dhcpd/dhcpd.h10
-rw-r--r--usr.sbin/dhcpd/options.c10
-rw-r--r--usr.sbin/dhcpd/udpsock.c172
9 files changed, 249 insertions, 25 deletions
diff --git a/usr.sbin/dhcpd/Makefile b/usr.sbin/dhcpd/Makefile
index 341ed8a31d4..6f0cfedf556 100644
--- a/usr.sbin/dhcpd/Makefile
+++ b/usr.sbin/dhcpd/Makefile
@@ -1,10 +1,11 @@
-# $OpenBSD: Makefile,v 1.4 2008/05/07 12:19:20 beck Exp $
+# $OpenBSD: Makefile,v 1.5 2014/07/11 09:42:27 yasuoka Exp $
.include <bsd.own.mk>
SRCS= bootp.c confpars.c db.c dhcp.c dhcpd.c bpf.c packet.c errwarn.c \
dispatch.c print.c memory.c options.c inet.c conflex.c parse.c \
- alloc.c tables.c tree.c hash.c convert.c icmp.c pfutils.c sync.c
+ alloc.c tables.c tree.c hash.c convert.c icmp.c pfutils.c sync.c \
+ udpsock.c
PROG= dhcpd
MAN= dhcpd.8 dhcpd.conf.5 dhcpd.leases.5 dhcp-options.5
diff --git a/usr.sbin/dhcpd/bootp.c b/usr.sbin/dhcpd/bootp.c
index 319736a5ab2..c47664ffb9b 100644
--- a/usr.sbin/dhcpd/bootp.c
+++ b/usr.sbin/dhcpd/bootp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: bootp.c,v 1.14 2014/06/11 16:45:15 pelikan Exp $ */
+/* $OpenBSD: bootp.c,v 1.15 2014/07/11 09:42:27 yasuoka Exp $ */
/*
* BOOTP Protocol support.
@@ -325,7 +325,7 @@ lose:
to.sin_addr = raw.giaddr;
to.sin_port = server_port;
- (void) send_packet(packet->interface, &raw,
+ (void) packet->interface->send_packet(packet->interface, &raw,
outgoing.packet_length, from, &to, packet->haddr);
return;
}
@@ -345,6 +345,6 @@ lose:
}
errno = 0;
- (void) send_packet(packet->interface, &raw,
+ (void) packet->interface->send_packet(packet->interface, &raw,
outgoing.packet_length, from, &to, packet->haddr);
}
diff --git a/usr.sbin/dhcpd/bpf.c b/usr.sbin/dhcpd/bpf.c
index e63c9efe5d6..fc146a31649 100644
--- a/usr.sbin/dhcpd/bpf.c
+++ b/usr.sbin/dhcpd/bpf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: bpf.c,v 1.10 2013/04/05 19:31:36 krw Exp $ */
+/* $OpenBSD: bpf.c,v 1.11 2014/07/11 09:42:27 yasuoka Exp $ */
/* BPF socket interface code, originally contributed by Archie Cobbs. */
@@ -52,6 +52,9 @@
#define BPF_FORMAT "/dev/bpf%d"
+ssize_t send_packet (struct interface_info *, struct dhcp_packet *,
+ size_t, struct in_addr, struct sockaddr_in *, struct hardware *);
+
/*
* Called by get_interface_list for each interface that's discovered.
* Opens a packet filter for each interface and adds it to the select
@@ -81,6 +84,7 @@ if_register_bpf(struct interface_info *info)
error("Can't attach interface %s to bpf device %s: %m",
info->name, filename);
+ info->send_packet = send_packet;
return (sock);
}
diff --git a/usr.sbin/dhcpd/dhcp.c b/usr.sbin/dhcpd/dhcp.c
index bfef5a6a0cb..8370affb09b 100644
--- a/usr.sbin/dhcpd/dhcp.c
+++ b/usr.sbin/dhcpd/dhcp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: dhcp.c,v 1.36 2013/04/05 19:31:36 krw Exp $ */
+/* $OpenBSD: dhcp.c,v 1.37 2014/07/11 09:42:27 yasuoka Exp $ */
/*
* Copyright (c) 1995, 1996, 1997, 1998, 1999
@@ -652,7 +652,7 @@ nak_lease(struct packet *packet, struct iaddr *cip)
to.sin_addr = raw.giaddr;
to.sin_port = server_port;
- result = send_packet(packet->interface, &raw,
+ result = packet->interface->send_packet(packet->interface, &raw,
outgoing.packet_length, from, &to, packet->haddr);
if (result == -1)
warning("send_fallback: %m");
@@ -663,7 +663,7 @@ nak_lease(struct packet *packet, struct iaddr *cip)
}
errno = 0;
- result = send_packet(packet->interface, &raw,
+ result = packet->interface->send_packet(packet->interface, &raw,
outgoing.packet_length, from, &to, NULL);
}
@@ -1327,7 +1327,7 @@ dhcp_reply(struct lease *lease)
memcpy(&from, state->from.iabuf, sizeof from);
- (void) send_packet(state->ip, &raw,
+ (void) state->ip->send_packet(state->ip, &raw,
packet_length, from, &to, &state->haddr);
free_lease_state(state, "dhcp_reply gateway");
@@ -1371,7 +1371,7 @@ dhcp_reply(struct lease *lease)
memcpy(&from, state->from.iabuf, sizeof from);
- (void) send_packet(state->ip, &raw, packet_length,
+ (void) state->ip->send_packet(state->ip, &raw, packet_length,
from, &to, &state->haddr);
free_lease_state(state, "dhcp_reply");
diff --git a/usr.sbin/dhcpd/dhcpd.8 b/usr.sbin/dhcpd/dhcpd.8
index 1dba28aff50..b51e08d38c2 100644
--- a/usr.sbin/dhcpd/dhcpd.8
+++ b/usr.sbin/dhcpd/dhcpd.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: dhcpd.8,v 1.22 2014/01/03 16:21:58 jmc Exp $
+.\" $OpenBSD: dhcpd.8,v 1.23 2014/07/11 09:42:27 yasuoka Exp $
.\"
.\" Copyright (c) 1995, 1996 The Internet Software Consortium.
.\" All rights reserved.
@@ -36,7 +36,7 @@
.\" see ``http://www.isc.org/''. To learn more about Vixie
.\" Enterprises, see ``http://www.vix.com''.
.\"
-.Dd $Mdocdate: January 3 2014 $
+.Dd $Mdocdate: July 11 2014 $
.Dt DHCPD 8
.Os
.Sh NAME
@@ -46,6 +46,7 @@
.Nm dhcpd
.Bk -words
.Op Fl dfn
+.Op Fl u Ns Op Ar bind_address
.Op Fl A Ar abandoned_ip_table
.Op Fl C Ar changed_ip_table
.Op Fl c Ar config-file
@@ -149,7 +150,9 @@ This should be done on systems where
.Nm
is unable to identify non-broadcast interfaces,
but should not be required on other systems.
-If no interface names are specified on the command line,
+If any interface name and
+.Fl u
+is not specified on the command line,
.Nm
will identify all network interfaces which are up, eliminating non-broadcast
interfaces if possible, and listen for DHCP broadcasts on each interface.
@@ -239,6 +242,24 @@ for testing lease files in a non-production environment.
.It Fl n
Only test configuration, do not run
.Nm .
+.It Fl u Ns Op Ar bind_address
+Use an UDP socket instead of BPF for receiving and sending packets.
+Only DHCPINFORM messages can be handled on this socket, other messages are
+discarded.
+With this option,
+.Nm
+can answer DHCPINFORM from the clients on on non ethernet interfaces
+(eg.
+.Xr tun 4
+or
+.Xr pppx 4
+.Ns ) .
+If
+.Ar bind_address
+is specified,
+.Nm
+will bind that address, otherwise
+the limited broadcast address (255.255.255.255) is used as default.
.It Fl Y Ar synctarget
Add target
.Ar synctarget
diff --git a/usr.sbin/dhcpd/dhcpd.c b/usr.sbin/dhcpd/dhcpd.c
index 046504feccd..8c854bbf9dd 100644
--- a/usr.sbin/dhcpd/dhcpd.c
+++ b/usr.sbin/dhcpd/dhcpd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: dhcpd.c,v 1.44 2014/05/07 13:20:47 pelikan Exp $ */
+/* $OpenBSD: dhcpd.c,v 1.45 2014/07/11 09:42:27 yasuoka Exp $ */
/*
* Copyright (c) 2004 Henning Brauer <henning@cvs.openbsd.org>
@@ -71,18 +71,19 @@ struct syslog_data sdata = SYSLOG_DATA_INIT;
int
main(int argc, char *argv[])
{
- int ch, cftest = 0, daemonize = 1, rdomain = -1;
+ int ch, cftest = 0, daemonize = 1, rdomain = -1, udpsockmode = 0;
extern char *__progname;
char *sync_iface = NULL;
char *sync_baddr = NULL;
u_short sync_port = 0;
struct servent *ent;
+ struct in_addr udpaddr;
/* Initially, log errors to stderr as well as to syslogd. */
openlog_r(__progname, LOG_PID | LOG_NDELAY, DHCPD_LOG_FACILITY, &sdata);
opterr = 0;
- while ((ch = getopt(argc, argv, "A:C:L:c:dfl:nY:y:")) != -1)
+ while ((ch = getopt(argc, argv, "A:C:L:c:dfl:nu::Y:y:")) != -1)
switch (ch) {
case 'Y':
syncsend = 1;
@@ -98,8 +99,10 @@ main(int argc, char *argv[])
sync_port = ntohs(ent->s_port);
}
+ udpaddr.s_addr = htonl(INADDR_BROADCAST);
+
optreset = optind = opterr = 1;
- while ((ch = getopt(argc, argv, "A:C:L:c:dfl:nY:y:")) != -1)
+ while ((ch = getopt(argc, argv, "A:C:L:c:dfl:nu::Y:y:")) != -1)
switch (ch) {
case 'A':
abandoned_tab = optarg;
@@ -128,6 +131,14 @@ main(int argc, char *argv[])
cftest = 1;
log_perror = 1;
break;
+ case 'u':
+ udpsockmode = 1;
+ if (optarg != NULL) {
+ if (inet_aton(optarg, &udpaddr) != 1)
+ errx(1, "Cannot parse binding IP "
+ "address: %s", optarg);
+ }
+ break;
case 'Y':
if (sync_addhost(optarg, sync_port) != 0)
sync_iface = optarg;
@@ -169,12 +180,15 @@ main(int argc, char *argv[])
exit(0);
db_startup();
- discover_interfaces(&rdomain);
+ if (!udpsockmode || argc > 0)
+ discover_interfaces(&rdomain);
if (rdomain != -1)
if (setrtable(rdomain) == -1)
error("setrtable (%m)");
+ if (udpsockmode)
+ udpsock_startup(udpaddr);
icmp_startup(1, lease_pinged);
if (syncsend || syncrecv) {
@@ -235,8 +249,8 @@ usage(void)
fprintf(stderr, "usage: %s [-dfn] [-A abandoned_ip_table]", __progname);
fprintf(stderr, " [-C changed_ip_table]\n");
fprintf(stderr, "\t[-c config-file] [-L leased_ip_table]");
- fprintf(stderr, " [-l lease-file] [-Y synctarget] [-y synclisten]\n");
- fprintf(stderr, "\t[if0 [... ifN]]\n");
+ fprintf(stderr, " [-l lease-file] [-u[bind_address]]\n");
+ fprintf(stderr, "\t[-Y synctarget] [-y synclisten] [if0 [... ifN]]\n");
exit(1);
}
diff --git a/usr.sbin/dhcpd/dhcpd.h b/usr.sbin/dhcpd/dhcpd.h
index 03b367ba3f6..e13a52be339 100644
--- a/usr.sbin/dhcpd/dhcpd.h
+++ b/usr.sbin/dhcpd/dhcpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: dhcpd.h,v 1.50 2014/05/07 13:20:47 pelikan Exp $ */
+/* $OpenBSD: dhcpd.h,v 1.51 2014/07/11 09:42:27 yasuoka Exp $ */
/*
* Copyright (c) 1995, 1996, 1997, 1998, 1999
@@ -424,6 +424,9 @@ struct interface_info {
int errors;
int dead;
u_int16_t index;
+ int is_udpsock;
+ ssize_t (*send_packet)(struct interface_info *, struct dhcp_packet *,
+ size_t, struct in_addr, struct sockaddr_in *, struct hardware *);
};
struct hardware_link {
@@ -616,8 +619,6 @@ char *print_hw_addr(int, int, unsigned char *);
/* bpf.c */
int if_register_bpf(struct interface_info *);
void if_register_send(struct interface_info *);
-ssize_t send_packet(struct interface_info *, struct dhcp_packet *, size_t,
- struct in_addr, struct sockaddr_in *, struct hardware *);
void if_register_receive(struct interface_info *);
ssize_t receive_packet(struct interface_info *, unsigned char *, size_t,
struct sockaddr_in *, struct hardware *);
@@ -697,3 +698,6 @@ size_t atomicio(ssize_t (*)(int, void *, size_t), int, void *, size_t);
#define vwrite (ssize_t (*)(int, void *, size_t))write
void pfmsg(char, struct lease *);
extern struct syslog_data sdata;
+
+/* udpsock.c */
+void udpsock_startup(struct in_addr);
diff --git a/usr.sbin/dhcpd/options.c b/usr.sbin/dhcpd/options.c
index a42c688e466..91f6395425b 100644
--- a/usr.sbin/dhcpd/options.c
+++ b/usr.sbin/dhcpd/options.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: options.c,v 1.26 2010/01/02 04:21:16 krw Exp $ */
+/* $OpenBSD: options.c,v 1.27 2014/07/11 09:42:27 yasuoka Exp $ */
/* DHCP options parsing and reassembly. */
@@ -509,6 +509,14 @@ do_packet(struct interface_info *interface, struct dhcp_packet *packet,
if (tp.options_valid &&
tp.options[DHO_DHCP_MESSAGE_TYPE].data)
tp.packet_type = tp.options[DHO_DHCP_MESSAGE_TYPE].data[0];
+
+ if (interface->is_udpsock) {
+ if (tp.packet_type != DHCPINFORM) {
+ note("Unable to handle a DHCP message type=%d on UDP "
+ "socket", tp.packet_type);
+ return;
+ }
+ }
if (tp.packet_type)
dhcp(&tp);
else
diff --git a/usr.sbin/dhcpd/udpsock.c b/usr.sbin/dhcpd/udpsock.c
new file mode 100644
index 00000000000..548767a14b0
--- /dev/null
+++ b/usr.sbin/dhcpd/udpsock.c
@@ -0,0 +1,172 @@
+/* $OpenBSD: udpsock.c,v 1.1 2014/07/11 09:42:27 yasuoka Exp $ */
+
+/*
+ * Copyright (c) 2014 YASUOKA Masahiko <yasuoka@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "dhcpd.h"
+
+void udpsock_handler (struct protocol *);
+ssize_t udpsock_send_packet(struct interface_info *, struct dhcp_packet *,
+ size_t, struct in_addr, struct sockaddr_in *, struct hardware *);
+
+struct udpsock {
+ int sock;
+};
+
+void
+udpsock_startup(struct in_addr bindaddr)
+{
+ int sock, onoff;
+ struct sockaddr_in sin4;
+ struct udpsock *udpsock;
+
+ if ((udpsock = calloc(1, sizeof(struct udpsock))) == NULL)
+ error("could not create udpsock: %s", strerror(errno));
+
+ memset(&sin4, 0, sizeof(sin4));
+ if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+ error("creating a socket failed for udp: %s", strerror(errno));
+
+ onoff = 1;
+ if (setsockopt(sock, IPPROTO_IP, IP_RECVIF, &onoff, sizeof(onoff)) != 0)
+ error("setsocketopt IP_RECVIF failed for udp: %s",
+ strerror(errno));
+
+ sin4.sin_family = AF_INET;
+ sin4.sin_len = sizeof(sin4);
+ sin4.sin_addr = bindaddr;
+ sin4.sin_port = server_port;
+
+ if (bind(sock, (struct sockaddr *)&sin4, sizeof(sin4)) != 0)
+ error("bind failed for udp: %s", strerror(errno));
+
+ add_protocol("udp", sock, udpsock_handler, (void *)(intptr_t)udpsock);
+ note("Listening on %s:%d/udp.", inet_ntoa(sin4.sin_addr),
+ ntohs(server_port));
+
+ udpsock->sock = sock;
+}
+
+void
+udpsock_handler(struct protocol *protocol)
+{
+ int sockio;
+ char cbuf[256], ifname[IF_NAMESIZE];
+ ssize_t len;
+ struct udpsock *udpsock = protocol->local;
+ struct msghdr m;
+ struct cmsghdr *cm;
+ struct iovec iov[1];
+ struct sockaddr_storage ss;
+ struct sockaddr_in *sin4;
+ struct sockaddr_dl *sdl = NULL;
+ struct interface_info iface;
+ struct iaddr from, addr;
+ unsigned char packetbuf[4095];
+ struct dhcp_packet *packet = (struct dhcp_packet *)packetbuf;
+ struct hardware hw;
+ struct ifreq ifr;
+ struct subnet *subnet;
+
+ memset(&hw, 0, sizeof(hw));
+
+ iov[0].iov_base = packetbuf;
+ iov[0].iov_len = sizeof(packetbuf);
+ memset(&m, 0, sizeof(m));
+ m.msg_name = &ss;
+ m.msg_namelen = sizeof(ss);
+ m.msg_iov = iov;
+ m.msg_iovlen = nitems(iov);
+ m.msg_control = cbuf;
+ m.msg_controllen = sizeof(cbuf);
+
+ memset(&iface, 0, sizeof(iface));
+ if ((len = recvmsg(udpsock->sock, &m, 0)) < 0) {
+ warning("receiving a DHCP message failed: %s", strerror(errno));
+ return;
+ }
+ if (ss.ss_family != AF_INET) {
+ warning("received DHCP message is not AF_INET");
+ return;
+ }
+ sin4 = (struct sockaddr_in *)&ss;
+ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&m);
+ m.msg_controllen != 0 && cm;
+ cm = (struct cmsghdr *)CMSG_NXTHDR(&m, cm)) {
+ if (cm->cmsg_level == IPPROTO_IP &&
+ cm->cmsg_type == IP_RECVIF)
+ sdl = (struct sockaddr_dl *)CMSG_DATA(cm);
+ }
+ if (sdl == NULL) {
+ warning("could not get the received interface by IP_RECVIF");
+ return;
+ }
+ if_indextoname(sdl->sdl_index, ifname);
+
+ if ((sockio = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ warning("socket creation failed: %s", strerror(errno));
+ return;
+ }
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(sockio, SIOCGIFADDR, &ifr, sizeof(ifr)) != 0) {
+ warning("Failed to get address for %s: %s", ifname,
+ strerror(errno));
+ close(sockio);
+ return;
+ }
+ close(sockio);
+
+ if (ifr.ifr_addr.sa_family != AF_INET)
+ return;
+
+ iface.is_udpsock = 1;
+ iface.send_packet = udpsock_send_packet;
+ iface.wfdesc = udpsock->sock;
+ iface.ifp = &ifr;
+ iface.index = sdl->sdl_index;
+ iface.primary_address = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr;
+ strlcpy(iface.name, ifname, sizeof(iface.name));
+
+ addr.len = 4;
+ memcpy(&addr.iabuf, &iface.primary_address, addr.len);
+
+ if ((subnet = find_subnet(addr)) == NULL)
+ return;
+ iface.shared_network = subnet->shared_network ;
+ from.len = 4;
+ memcpy(&from.iabuf, &sin4->sin_addr, from.len);
+ do_packet(&iface, packet, len, sin4->sin_port, from, &hw);
+}
+
+ssize_t
+udpsock_send_packet(struct interface_info *interface, struct dhcp_packet *raw,
+ size_t len, struct in_addr from, struct sockaddr_in *to,
+ struct hardware *hto)
+{
+ return (sendto(interface->wfdesc, raw, len, 0, (struct sockaddr *)to,
+ sizeof(struct sockaddr_in)));
+}