diff options
author | YASUOKA Masahiko <yasuoka@cvs.openbsd.org> | 2014-07-11 09:42:28 +0000 |
---|---|---|
committer | YASUOKA Masahiko <yasuoka@cvs.openbsd.org> | 2014-07-11 09:42:28 +0000 |
commit | f070257addf0e815b57d7380d6ae2b71fdb65679 (patch) | |
tree | 2851b64f28c2381a9bdf4eb341d6b1d25fc6b6f2 | |
parent | 6f99326d0e5b7d2b86664a411cbc72ead1ab7930 (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
-rw-r--r-- | usr.sbin/dhcpd/Makefile | 5 | ||||
-rw-r--r-- | usr.sbin/dhcpd/bootp.c | 6 | ||||
-rw-r--r-- | usr.sbin/dhcpd/bpf.c | 6 | ||||
-rw-r--r-- | usr.sbin/dhcpd/dhcp.c | 10 | ||||
-rw-r--r-- | usr.sbin/dhcpd/dhcpd.8 | 27 | ||||
-rw-r--r-- | usr.sbin/dhcpd/dhcpd.c | 28 | ||||
-rw-r--r-- | usr.sbin/dhcpd/dhcpd.h | 10 | ||||
-rw-r--r-- | usr.sbin/dhcpd/options.c | 10 | ||||
-rw-r--r-- | usr.sbin/dhcpd/udpsock.c | 172 |
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 = 𝔦 + 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))); +} |