xref: /openbsd-src/usr.sbin/dhcpd/udpsock.c (revision df69c215c7c66baf660f3f65414fd34796c96152)
1*df69c215Sderaadt /*	$OpenBSD: udpsock.c,v 1.11 2019/06/28 13:32:47 deraadt Exp $	*/
284d8c049Syasuoka 
384d8c049Syasuoka /*
484d8c049Syasuoka  * Copyright (c) 2014 YASUOKA Masahiko <yasuoka@openbsd.org>
584d8c049Syasuoka  *
684d8c049Syasuoka  * Permission to use, copy, modify, and distribute this software for any
784d8c049Syasuoka  * purpose with or without fee is hereby granted, provided that the above
884d8c049Syasuoka  * copyright notice and this permission notice appear in all copies.
984d8c049Syasuoka  *
1084d8c049Syasuoka  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1184d8c049Syasuoka  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1284d8c049Syasuoka  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1384d8c049Syasuoka  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1484d8c049Syasuoka  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1584d8c049Syasuoka  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1684d8c049Syasuoka  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1784d8c049Syasuoka  */
1884d8c049Syasuoka 
195a738b33Skrw #include <sys/types.h>
2084d8c049Syasuoka #include <sys/ioctl.h>
21837cddffSkrw #include <sys/socket.h>
22837cddffSkrw 
2384d8c049Syasuoka #include <arpa/inet.h>
2484d8c049Syasuoka 
25837cddffSkrw #include <net/if.h>
26837cddffSkrw #include <net/if_dl.h>
2784d8c049Syasuoka 
28837cddffSkrw #include <netinet/in.h>
29837cddffSkrw 
30837cddffSkrw #include <ctype.h>
31837cddffSkrw #include <errno.h>
32837cddffSkrw #include <stdio.h>
33837cddffSkrw #include <stdlib.h>
34837cddffSkrw #include <string.h>
35837cddffSkrw #include <unistd.h>
36837cddffSkrw 
37837cddffSkrw #include "dhcp.h"
38837cddffSkrw #include "tree.h"
3984d8c049Syasuoka #include "dhcpd.h"
40c525a185Skrw #include "log.h"
4184d8c049Syasuoka 
4284d8c049Syasuoka void	 udpsock_handler (struct protocol *);
4384d8c049Syasuoka ssize_t	 udpsock_send_packet(struct interface_info *, struct dhcp_packet *,
4484d8c049Syasuoka     size_t, struct in_addr, struct sockaddr_in *, struct hardware *);
4584d8c049Syasuoka 
4684d8c049Syasuoka struct udpsock {
4784d8c049Syasuoka 	int	 sock;
4884d8c049Syasuoka };
4984d8c049Syasuoka 
5084d8c049Syasuoka void
udpsock_startup(struct in_addr bindaddr)5184d8c049Syasuoka udpsock_startup(struct in_addr bindaddr)
5284d8c049Syasuoka {
5384d8c049Syasuoka 	int			 sock, onoff;
5484d8c049Syasuoka 	struct sockaddr_in	 sin4;
5584d8c049Syasuoka 	struct udpsock		*udpsock;
5684d8c049Syasuoka 
5784d8c049Syasuoka 	if ((udpsock = calloc(1, sizeof(struct udpsock))) == NULL)
58a76b277aSkrw 		fatal("could not create udpsock");
5984d8c049Syasuoka 
6084d8c049Syasuoka 	memset(&sin4, 0, sizeof(sin4));
61*df69c215Sderaadt 	if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
62a76b277aSkrw 		fatal("creating a socket failed for udp");
6384d8c049Syasuoka 
6484d8c049Syasuoka 	onoff = 1;
6535318e8fSkrw 	if (setsockopt(sock, IPPROTO_IP, IP_RECVIF, &onoff, sizeof(onoff)) !=
6635318e8fSkrw 	    0)
67a76b277aSkrw 		fatal("setsocketopt IP_RECVIF failed for udp");
6884d8c049Syasuoka 
6984d8c049Syasuoka 	sin4.sin_family = AF_INET;
7084d8c049Syasuoka 	sin4.sin_len = sizeof(sin4);
7184d8c049Syasuoka 	sin4.sin_addr = bindaddr;
7284d8c049Syasuoka 	sin4.sin_port = server_port;
7384d8c049Syasuoka 
7484d8c049Syasuoka 	if (bind(sock, (struct sockaddr *)&sin4, sizeof(sin4)) != 0)
75a76b277aSkrw 		fatal("bind failed for udp");
7684d8c049Syasuoka 
7784d8c049Syasuoka 	add_protocol("udp", sock, udpsock_handler, (void *)(intptr_t)udpsock);
78c525a185Skrw 	log_info("Listening on %s:%d/udp.", inet_ntoa(sin4.sin_addr),
7984d8c049Syasuoka 	    ntohs(server_port));
8084d8c049Syasuoka 
8184d8c049Syasuoka 	udpsock->sock = sock;
8284d8c049Syasuoka }
8384d8c049Syasuoka 
8484d8c049Syasuoka void
udpsock_handler(struct protocol * protocol)8584d8c049Syasuoka udpsock_handler(struct protocol *protocol)
8684d8c049Syasuoka {
8784d8c049Syasuoka 	int			 sockio;
8884d8c049Syasuoka 	char			 cbuf[256], ifname[IF_NAMESIZE];
8984d8c049Syasuoka 	ssize_t			 len;
9084d8c049Syasuoka 	struct udpsock		*udpsock = protocol->local;
9184d8c049Syasuoka 	struct msghdr		 m;
9284d8c049Syasuoka 	struct cmsghdr		*cm;
9384d8c049Syasuoka 	struct iovec		 iov[1];
9484d8c049Syasuoka 	struct sockaddr_storage	 ss;
9584d8c049Syasuoka 	struct sockaddr_in	*sin4;
9684d8c049Syasuoka 	struct sockaddr_dl	*sdl = NULL;
9784d8c049Syasuoka 	struct interface_info	 iface;
9884d8c049Syasuoka 	struct iaddr		 from, addr;
9984d8c049Syasuoka 	unsigned char		 packetbuf[4095];
10084d8c049Syasuoka 	struct dhcp_packet	*packet = (struct dhcp_packet *)packetbuf;
10184d8c049Syasuoka 	struct hardware		 hw;
10284d8c049Syasuoka 	struct ifreq		 ifr;
10384d8c049Syasuoka 	struct subnet		*subnet;
10484d8c049Syasuoka 
10584d8c049Syasuoka 	memset(&hw, 0, sizeof(hw));
10684d8c049Syasuoka 
10784d8c049Syasuoka 	iov[0].iov_base = packetbuf;
10884d8c049Syasuoka 	iov[0].iov_len = sizeof(packetbuf);
10984d8c049Syasuoka 	memset(&m, 0, sizeof(m));
11084d8c049Syasuoka 	m.msg_name = &ss;
11184d8c049Syasuoka 	m.msg_namelen = sizeof(ss);
11284d8c049Syasuoka 	m.msg_iov = iov;
1135a738b33Skrw 	m.msg_iovlen = 1;
11484d8c049Syasuoka 	m.msg_control = cbuf;
11584d8c049Syasuoka 	m.msg_controllen = sizeof(cbuf);
11684d8c049Syasuoka 
11784d8c049Syasuoka 	memset(&iface, 0, sizeof(iface));
118*df69c215Sderaadt 	if ((len = recvmsg(udpsock->sock, &m, 0)) == -1) {
119a76b277aSkrw 		log_warn("receiving a DHCP message failed");
12084d8c049Syasuoka 		return;
12184d8c049Syasuoka 	}
12284d8c049Syasuoka 	if (ss.ss_family != AF_INET) {
123c525a185Skrw 		log_warnx("received DHCP message is not AF_INET");
12484d8c049Syasuoka 		return;
12584d8c049Syasuoka 	}
12684d8c049Syasuoka 	sin4 = (struct sockaddr_in *)&ss;
12784d8c049Syasuoka 	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&m);
12884d8c049Syasuoka 	    m.msg_controllen != 0 && cm;
12984d8c049Syasuoka 	    cm = (struct cmsghdr *)CMSG_NXTHDR(&m, cm)) {
13084d8c049Syasuoka 		if (cm->cmsg_level == IPPROTO_IP &&
13184d8c049Syasuoka 		    cm->cmsg_type == IP_RECVIF)
13284d8c049Syasuoka 			sdl = (struct sockaddr_dl *)CMSG_DATA(cm);
13384d8c049Syasuoka 	}
13484d8c049Syasuoka 	if (sdl == NULL) {
135c525a185Skrw 		log_warnx("could not get the received interface by IP_RECVIF");
13684d8c049Syasuoka 		return;
13784d8c049Syasuoka 	}
13884d8c049Syasuoka 	if_indextoname(sdl->sdl_index, ifname);
13984d8c049Syasuoka 
140*df69c215Sderaadt 	if ((sockio = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
141a76b277aSkrw 		log_warn("socket creation failed");
14284d8c049Syasuoka 		return;
14384d8c049Syasuoka 	}
14484d8c049Syasuoka 	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
145*df69c215Sderaadt 	if (ioctl(sockio, SIOCGIFADDR, &ifr, sizeof(ifr)) == -1) {
146a76b277aSkrw 		log_warn("Failed to get address for %s", ifname);
14784d8c049Syasuoka 		close(sockio);
14884d8c049Syasuoka 		return;
14984d8c049Syasuoka 	}
15084d8c049Syasuoka 	close(sockio);
15184d8c049Syasuoka 
15284d8c049Syasuoka 	if (ifr.ifr_addr.sa_family != AF_INET)
15384d8c049Syasuoka 		return;
15484d8c049Syasuoka 
15584d8c049Syasuoka 	iface.is_udpsock = 1;
15684d8c049Syasuoka 	iface.send_packet = udpsock_send_packet;
15784d8c049Syasuoka 	iface.wfdesc = udpsock->sock;
15884d8c049Syasuoka 	iface.ifp = &ifr;
15984d8c049Syasuoka 	iface.index = sdl->sdl_index;
16035318e8fSkrw 	iface.primary_address =
16135318e8fSkrw 	    ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr;
16284d8c049Syasuoka 	strlcpy(iface.name, ifname, sizeof(iface.name));
16384d8c049Syasuoka 
16484d8c049Syasuoka 	addr.len = 4;
16584d8c049Syasuoka 	memcpy(&addr.iabuf, &iface.primary_address, addr.len);
16684d8c049Syasuoka 
16784d8c049Syasuoka 	if ((subnet = find_subnet(addr)) == NULL)
16884d8c049Syasuoka 		return;
16984d8c049Syasuoka 	iface.shared_network = subnet->shared_network ;
17084d8c049Syasuoka 	from.len = 4;
17184d8c049Syasuoka 	memcpy(&from.iabuf, &sin4->sin_addr, from.len);
17284d8c049Syasuoka 	do_packet(&iface, packet, len, sin4->sin_port, from, &hw);
17384d8c049Syasuoka }
17484d8c049Syasuoka 
17584d8c049Syasuoka 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)17684d8c049Syasuoka udpsock_send_packet(struct interface_info *interface, struct dhcp_packet *raw,
17784d8c049Syasuoka     size_t len, struct in_addr from, struct sockaddr_in *to,
17884d8c049Syasuoka     struct hardware *hto)
17984d8c049Syasuoka {
18084d8c049Syasuoka 	return (sendto(interface->wfdesc, raw, len, 0, (struct sockaddr *)to,
18184d8c049Syasuoka 	    sizeof(struct sockaddr_in)));
18284d8c049Syasuoka }
183