1 /* $OpenBSD: udpsock.c,v 1.1 2014/07/11 09:42:27 yasuoka 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/param.h> 20 #include <sys/socket.h> 21 #include <sys/uio.h> 22 #include <sys/ioctl.h> 23 #include <netinet/in.h> 24 #include <arpa/inet.h> 25 26 #include <errno.h> 27 #include <stdint.h> 28 #include <string.h> 29 30 #include "dhcpd.h" 31 32 void udpsock_handler (struct protocol *); 33 ssize_t udpsock_send_packet(struct interface_info *, struct dhcp_packet *, 34 size_t, struct in_addr, struct sockaddr_in *, struct hardware *); 35 36 struct udpsock { 37 int sock; 38 }; 39 40 void 41 udpsock_startup(struct in_addr bindaddr) 42 { 43 int sock, onoff; 44 struct sockaddr_in sin4; 45 struct udpsock *udpsock; 46 47 if ((udpsock = calloc(1, sizeof(struct udpsock))) == NULL) 48 error("could not create udpsock: %s", strerror(errno)); 49 50 memset(&sin4, 0, sizeof(sin4)); 51 if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) 52 error("creating a socket failed for udp: %s", strerror(errno)); 53 54 onoff = 1; 55 if (setsockopt(sock, IPPROTO_IP, IP_RECVIF, &onoff, sizeof(onoff)) != 0) 56 error("setsocketopt IP_RECVIF failed for udp: %s", 57 strerror(errno)); 58 59 sin4.sin_family = AF_INET; 60 sin4.sin_len = sizeof(sin4); 61 sin4.sin_addr = bindaddr; 62 sin4.sin_port = server_port; 63 64 if (bind(sock, (struct sockaddr *)&sin4, sizeof(sin4)) != 0) 65 error("bind failed for udp: %s", strerror(errno)); 66 67 add_protocol("udp", sock, udpsock_handler, (void *)(intptr_t)udpsock); 68 note("Listening on %s:%d/udp.", inet_ntoa(sin4.sin_addr), 69 ntohs(server_port)); 70 71 udpsock->sock = sock; 72 } 73 74 void 75 udpsock_handler(struct protocol *protocol) 76 { 77 int sockio; 78 char cbuf[256], ifname[IF_NAMESIZE]; 79 ssize_t len; 80 struct udpsock *udpsock = protocol->local; 81 struct msghdr m; 82 struct cmsghdr *cm; 83 struct iovec iov[1]; 84 struct sockaddr_storage ss; 85 struct sockaddr_in *sin4; 86 struct sockaddr_dl *sdl = NULL; 87 struct interface_info iface; 88 struct iaddr from, addr; 89 unsigned char packetbuf[4095]; 90 struct dhcp_packet *packet = (struct dhcp_packet *)packetbuf; 91 struct hardware hw; 92 struct ifreq ifr; 93 struct subnet *subnet; 94 95 memset(&hw, 0, sizeof(hw)); 96 97 iov[0].iov_base = packetbuf; 98 iov[0].iov_len = sizeof(packetbuf); 99 memset(&m, 0, sizeof(m)); 100 m.msg_name = &ss; 101 m.msg_namelen = sizeof(ss); 102 m.msg_iov = iov; 103 m.msg_iovlen = nitems(iov); 104 m.msg_control = cbuf; 105 m.msg_controllen = sizeof(cbuf); 106 107 memset(&iface, 0, sizeof(iface)); 108 if ((len = recvmsg(udpsock->sock, &m, 0)) < 0) { 109 warning("receiving a DHCP message failed: %s", strerror(errno)); 110 return; 111 } 112 if (ss.ss_family != AF_INET) { 113 warning("received DHCP message is not AF_INET"); 114 return; 115 } 116 sin4 = (struct sockaddr_in *)&ss; 117 for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&m); 118 m.msg_controllen != 0 && cm; 119 cm = (struct cmsghdr *)CMSG_NXTHDR(&m, cm)) { 120 if (cm->cmsg_level == IPPROTO_IP && 121 cm->cmsg_type == IP_RECVIF) 122 sdl = (struct sockaddr_dl *)CMSG_DATA(cm); 123 } 124 if (sdl == NULL) { 125 warning("could not get the received interface by IP_RECVIF"); 126 return; 127 } 128 if_indextoname(sdl->sdl_index, ifname); 129 130 if ((sockio = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 131 warning("socket creation failed: %s", strerror(errno)); 132 return; 133 } 134 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 135 if (ioctl(sockio, SIOCGIFADDR, &ifr, sizeof(ifr)) != 0) { 136 warning("Failed to get address for %s: %s", ifname, 137 strerror(errno)); 138 close(sockio); 139 return; 140 } 141 close(sockio); 142 143 if (ifr.ifr_addr.sa_family != AF_INET) 144 return; 145 146 iface.is_udpsock = 1; 147 iface.send_packet = udpsock_send_packet; 148 iface.wfdesc = udpsock->sock; 149 iface.ifp = 𝔦 150 iface.index = sdl->sdl_index; 151 iface.primary_address = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr; 152 strlcpy(iface.name, ifname, sizeof(iface.name)); 153 154 addr.len = 4; 155 memcpy(&addr.iabuf, &iface.primary_address, addr.len); 156 157 if ((subnet = find_subnet(addr)) == NULL) 158 return; 159 iface.shared_network = subnet->shared_network ; 160 from.len = 4; 161 memcpy(&from.iabuf, &sin4->sin_addr, from.len); 162 do_packet(&iface, packet, len, sin4->sin_port, from, &hw); 163 } 164 165 ssize_t 166 udpsock_send_packet(struct interface_info *interface, struct dhcp_packet *raw, 167 size_t len, struct in_addr from, struct sockaddr_in *to, 168 struct hardware *hto) 169 { 170 return (sendto(interface->wfdesc, raw, len, 0, (struct sockaddr *)to, 171 sizeof(struct sockaddr_in))); 172 } 173