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