1 /* $OpenBSD: udpsock.c,v 1.11 2019/06/28 13:32:47 deraadt 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 #include "log.h"
41
42 void udpsock_handler (struct protocol *);
43 ssize_t udpsock_send_packet(struct interface_info *, struct dhcp_packet *,
44 size_t, struct in_addr, struct sockaddr_in *, struct hardware *);
45
46 struct udpsock {
47 int sock;
48 };
49
50 void
udpsock_startup(struct in_addr bindaddr)51 udpsock_startup(struct in_addr bindaddr)
52 {
53 int sock, onoff;
54 struct sockaddr_in sin4;
55 struct udpsock *udpsock;
56
57 if ((udpsock = calloc(1, sizeof(struct udpsock))) == NULL)
58 fatal("could not create udpsock");
59
60 memset(&sin4, 0, sizeof(sin4));
61 if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
62 fatal("creating a socket failed for udp");
63
64 onoff = 1;
65 if (setsockopt(sock, IPPROTO_IP, IP_RECVIF, &onoff, sizeof(onoff)) !=
66 0)
67 fatal("setsocketopt IP_RECVIF failed for udp");
68
69 sin4.sin_family = AF_INET;
70 sin4.sin_len = sizeof(sin4);
71 sin4.sin_addr = bindaddr;
72 sin4.sin_port = server_port;
73
74 if (bind(sock, (struct sockaddr *)&sin4, sizeof(sin4)) != 0)
75 fatal("bind failed for udp");
76
77 add_protocol("udp", sock, udpsock_handler, (void *)(intptr_t)udpsock);
78 log_info("Listening on %s:%d/udp.", inet_ntoa(sin4.sin_addr),
79 ntohs(server_port));
80
81 udpsock->sock = sock;
82 }
83
84 void
udpsock_handler(struct protocol * protocol)85 udpsock_handler(struct protocol *protocol)
86 {
87 int sockio;
88 char cbuf[256], ifname[IF_NAMESIZE];
89 ssize_t len;
90 struct udpsock *udpsock = protocol->local;
91 struct msghdr m;
92 struct cmsghdr *cm;
93 struct iovec iov[1];
94 struct sockaddr_storage ss;
95 struct sockaddr_in *sin4;
96 struct sockaddr_dl *sdl = NULL;
97 struct interface_info iface;
98 struct iaddr from, addr;
99 unsigned char packetbuf[4095];
100 struct dhcp_packet *packet = (struct dhcp_packet *)packetbuf;
101 struct hardware hw;
102 struct ifreq ifr;
103 struct subnet *subnet;
104
105 memset(&hw, 0, sizeof(hw));
106
107 iov[0].iov_base = packetbuf;
108 iov[0].iov_len = sizeof(packetbuf);
109 memset(&m, 0, sizeof(m));
110 m.msg_name = &ss;
111 m.msg_namelen = sizeof(ss);
112 m.msg_iov = iov;
113 m.msg_iovlen = 1;
114 m.msg_control = cbuf;
115 m.msg_controllen = sizeof(cbuf);
116
117 memset(&iface, 0, sizeof(iface));
118 if ((len = recvmsg(udpsock->sock, &m, 0)) == -1) {
119 log_warn("receiving a DHCP message failed");
120 return;
121 }
122 if (ss.ss_family != AF_INET) {
123 log_warnx("received DHCP message is not AF_INET");
124 return;
125 }
126 sin4 = (struct sockaddr_in *)&ss;
127 for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&m);
128 m.msg_controllen != 0 && cm;
129 cm = (struct cmsghdr *)CMSG_NXTHDR(&m, cm)) {
130 if (cm->cmsg_level == IPPROTO_IP &&
131 cm->cmsg_type == IP_RECVIF)
132 sdl = (struct sockaddr_dl *)CMSG_DATA(cm);
133 }
134 if (sdl == NULL) {
135 log_warnx("could not get the received interface by IP_RECVIF");
136 return;
137 }
138 if_indextoname(sdl->sdl_index, ifname);
139
140 if ((sockio = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
141 log_warn("socket creation failed");
142 return;
143 }
144 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
145 if (ioctl(sockio, SIOCGIFADDR, &ifr, sizeof(ifr)) == -1) {
146 log_warn("Failed to get address for %s", ifname);
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 = 𝔦
159 iface.index = sdl->sdl_index;
160 iface.primary_address =
161 ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr;
162 strlcpy(iface.name, ifname, sizeof(iface.name));
163
164 addr.len = 4;
165 memcpy(&addr.iabuf, &iface.primary_address, addr.len);
166
167 if ((subnet = find_subnet(addr)) == NULL)
168 return;
169 iface.shared_network = subnet->shared_network ;
170 from.len = 4;
171 memcpy(&from.iabuf, &sin4->sin_addr, from.len);
172 do_packet(&iface, packet, len, sin4->sin_port, from, &hw);
173 }
174
175 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)176 udpsock_send_packet(struct interface_info *interface, struct dhcp_packet *raw,
177 size_t len, struct in_addr from, struct sockaddr_in *to,
178 struct hardware *hto)
179 {
180 return (sendto(interface->wfdesc, raw, len, 0, (struct sockaddr *)to,
181 sizeof(struct sockaddr_in)));
182 }
183