xref: /openbsd-src/usr.sbin/dhcpd/udpsock.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /*	$OpenBSD: udpsock.c,v 1.7 2016/04/27 10:16:10 mestre Exp $	*/
2 
3 /*
4  * Copyright (c) 2014 YASUOKA Masahiko <yasuoka@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/ioctl.h>
21 #include <sys/socket.h>
22 
23 #include <arpa/inet.h>
24 
25 #include <net/if.h>
26 #include <net/if_dl.h>
27 
28 #include <netinet/in.h>
29 
30 #include <ctype.h>
31 #include <errno.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 
37 #include "dhcp.h"
38 #include "tree.h"
39 #include "dhcpd.h"
40 
41 void	 udpsock_handler (struct protocol *);
42 ssize_t	 udpsock_send_packet(struct interface_info *, struct dhcp_packet *,
43     size_t, struct in_addr, struct sockaddr_in *, struct hardware *);
44 
45 struct udpsock {
46 	int	 sock;
47 };
48 
49 void
50 udpsock_startup(struct in_addr bindaddr)
51 {
52 	int			 sock, onoff;
53 	struct sockaddr_in	 sin4;
54 	struct udpsock		*udpsock;
55 
56 	if ((udpsock = calloc(1, sizeof(struct udpsock))) == NULL)
57 		error("could not create udpsock: %s", strerror(errno));
58 
59 	memset(&sin4, 0, sizeof(sin4));
60 	if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
61 		error("creating a socket failed for udp: %s", strerror(errno));
62 
63 	onoff = 1;
64 	if (setsockopt(sock, IPPROTO_IP, IP_RECVIF, &onoff, sizeof(onoff)) != 0)
65 		error("setsocketopt IP_RECVIF failed for udp: %s",
66 		    strerror(errno));
67 
68 	sin4.sin_family = AF_INET;
69 	sin4.sin_len = sizeof(sin4);
70 	sin4.sin_addr = bindaddr;
71 	sin4.sin_port = server_port;
72 
73 	if (bind(sock, (struct sockaddr *)&sin4, sizeof(sin4)) != 0)
74 		error("bind failed for udp: %s", strerror(errno));
75 
76 	add_protocol("udp", sock, udpsock_handler, (void *)(intptr_t)udpsock);
77 	note("Listening on %s:%d/udp.", inet_ntoa(sin4.sin_addr),
78 	    ntohs(server_port));
79 
80 	udpsock->sock = sock;
81 }
82 
83 void
84 udpsock_handler(struct protocol *protocol)
85 {
86 	int			 sockio;
87 	char			 cbuf[256], ifname[IF_NAMESIZE];
88 	ssize_t			 len;
89 	struct udpsock		*udpsock = protocol->local;
90 	struct msghdr		 m;
91 	struct cmsghdr		*cm;
92 	struct iovec		 iov[1];
93 	struct sockaddr_storage	 ss;
94 	struct sockaddr_in	*sin4;
95 	struct sockaddr_dl	*sdl = NULL;
96 	struct interface_info	 iface;
97 	struct iaddr		 from, addr;
98 	unsigned char		 packetbuf[4095];
99 	struct dhcp_packet	*packet = (struct dhcp_packet *)packetbuf;
100 	struct hardware		 hw;
101 	struct ifreq		 ifr;
102 	struct subnet		*subnet;
103 
104 	memset(&hw, 0, sizeof(hw));
105 
106 	iov[0].iov_base = packetbuf;
107 	iov[0].iov_len = sizeof(packetbuf);
108 	memset(&m, 0, sizeof(m));
109 	m.msg_name = &ss;
110 	m.msg_namelen = sizeof(ss);
111 	m.msg_iov = iov;
112 	m.msg_iovlen = 1;
113 	m.msg_control = cbuf;
114 	m.msg_controllen = sizeof(cbuf);
115 
116 	memset(&iface, 0, sizeof(iface));
117 	if ((len = recvmsg(udpsock->sock, &m, 0)) < 0) {
118 		warning("receiving a DHCP message failed: %s", strerror(errno));
119 		return;
120 	}
121 	if (ss.ss_family != AF_INET) {
122 		warning("received DHCP message is not AF_INET");
123 		return;
124 	}
125 	sin4 = (struct sockaddr_in *)&ss;
126 	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&m);
127 	    m.msg_controllen != 0 && cm;
128 	    cm = (struct cmsghdr *)CMSG_NXTHDR(&m, cm)) {
129 		if (cm->cmsg_level == IPPROTO_IP &&
130 		    cm->cmsg_type == IP_RECVIF)
131 			sdl = (struct sockaddr_dl *)CMSG_DATA(cm);
132 	}
133 	if (sdl == NULL) {
134 		warning("could not get the received interface by IP_RECVIF");
135 		return;
136 	}
137 	if_indextoname(sdl->sdl_index, ifname);
138 
139 	if ((sockio = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
140 		warning("socket creation failed: %s", strerror(errno));
141 		return;
142 	}
143 	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
144 	if (ioctl(sockio, SIOCGIFADDR, &ifr, sizeof(ifr)) != 0) {
145 		warning("Failed to get address for %s: %s", ifname,
146 		    strerror(errno));
147 		close(sockio);
148 		return;
149 	}
150 	close(sockio);
151 
152 	if (ifr.ifr_addr.sa_family != AF_INET)
153 		return;
154 
155 	iface.is_udpsock = 1;
156 	iface.send_packet = udpsock_send_packet;
157 	iface.wfdesc = udpsock->sock;
158 	iface.ifp = &ifr;
159 	iface.index = sdl->sdl_index;
160 	iface.primary_address = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr;
161 	strlcpy(iface.name, ifname, sizeof(iface.name));
162 
163 	addr.len = 4;
164 	memcpy(&addr.iabuf, &iface.primary_address, addr.len);
165 
166 	if ((subnet = find_subnet(addr)) == NULL)
167 		return;
168 	iface.shared_network = subnet->shared_network ;
169 	from.len = 4;
170 	memcpy(&from.iabuf, &sin4->sin_addr, from.len);
171 	do_packet(&iface, packet, len, sin4->sin_port, from, &hw);
172 }
173 
174 ssize_t
175 udpsock_send_packet(struct interface_info *interface, struct dhcp_packet *raw,
176     size_t len, struct in_addr from, struct sockaddr_in *to,
177     struct hardware *hto)
178 {
179 	return (sendto(interface->wfdesc, raw, len, 0, (struct sockaddr *)to,
180 	    sizeof(struct sockaddr_in)));
181 }
182