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 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 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 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