1 /* $NetBSD: main.c,v 1.4 2015/06/16 22:54:10 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2011 Antti Kantee. All Rights Reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/types.h> 29 #include <sys/ioctl.h> 30 #include <sys/socket.h> 31 #include <sys/sysctl.h> 32 33 #include <net/if.h> 34 #include <net/if_dl.h> 35 36 #include <err.h> 37 #include <errno.h> 38 #include <poll.h> 39 #include <stdlib.h> 40 #include <string.h> 41 42 #include <rump/rump_syscalls.h> 43 #include <rump/rumpclient.h> 44 45 #include "configure.h" 46 #include "dhcp.h" 47 #include "net.h" 48 49 struct interface *ifaces; 50 51 __dead static void 52 usage(void) 53 { 54 55 fprintf(stderr, "Usage: %s ifname\n", getprogname()); 56 exit(1); 57 } 58 59 int 60 get_hwaddr(struct interface *ifp) 61 { 62 struct if_laddrreq iflr; 63 struct sockaddr_dl *sdl; 64 int s, sverrno; 65 66 memset(&iflr, 0, sizeof(iflr)); 67 strlcpy(iflr.iflr_name, ifp->name, sizeof(iflr.iflr_name)); 68 iflr.addr.ss_family = AF_LINK; 69 70 sdl = satosdl(&iflr.addr); 71 sdl->sdl_alen = ETHER_ADDR_LEN; 72 73 if ((s = rump_sys_socket(AF_LINK, SOCK_DGRAM, 0)) == -1) 74 return -1; 75 76 if (rump_sys_ioctl(s, SIOCGLIFADDR, &iflr) == -1) { 77 sverrno = errno; 78 rump_sys_close(s); 79 errno = sverrno; 80 return -1; 81 } 82 83 /* XXX: is that the right way to copy the link address? */ 84 memcpy(ifp->hwaddr, sdl->sdl_data+strlen(ifp->name), ETHER_ADDR_LEN); 85 ifp->hwlen = ETHER_ADDR_LEN; 86 ifp->family = ARPHRD_ETHER; 87 88 rump_sys_close(s); 89 return 0; 90 } 91 92 static void 93 send_discover(struct interface *ifp) 94 { 95 struct dhcp_message *dhcp; 96 uint8_t *udp; 97 ssize_t mlen, ulen; 98 struct in_addr ia; 99 100 memset(&ia, 0, sizeof(ia)); 101 102 mlen = make_message(&dhcp, ifp, DHCP_DISCOVER); 103 ulen = make_udp_packet(&udp, (void *)dhcp, mlen, ia, ia); 104 if (send_raw_packet(ifp, ETHERTYPE_IP, udp, ulen) == -1) 105 err(EXIT_FAILURE, "sending discover failed"); 106 } 107 108 static void 109 send_request(struct interface *ifp) 110 { 111 struct dhcp_message *dhcp; 112 uint8_t *udp; 113 ssize_t mlen, ulen; 114 struct in_addr ia; 115 116 memset(&ia, 0, sizeof(ia)); 117 118 mlen = make_message(&dhcp, ifp, DHCP_REQUEST); 119 ulen = make_udp_packet(&udp, (void *)dhcp, mlen, ia, ia); 120 if (send_raw_packet(ifp, ETHERTYPE_IP, udp, ulen) == -1) 121 err(EXIT_FAILURE, "sending discover failed"); 122 } 123 124 /* wait for 5s by default */ 125 #define RESPWAIT 5000 126 static void 127 get_network(struct interface *ifp, uint8_t **rawp, 128 const struct dhcp_message **dhcpp) 129 { 130 struct pollfd pfd; 131 const struct dhcp_message *dhcp; 132 const uint8_t *data; 133 uint8_t *raw; 134 ssize_t n; 135 136 pfd.fd = ifp->raw_fd; 137 pfd.events = POLLIN; 138 139 raw = xmalloc(udp_dhcp_len); 140 for (;;) { 141 switch (rump_sys_poll(&pfd, 1, RESPWAIT)) { 142 case 0: 143 errx(EXIT_FAILURE, "timed out waiting for response"); 144 case -1: 145 err(EXIT_FAILURE, "poll failed"); 146 default: 147 break; 148 } 149 150 if ((n = get_raw_packet(ifp, ETHERTYPE_IP, 151 raw, udp_dhcp_len)) < 1) 152 continue; 153 154 if (valid_udp_packet(raw, n, NULL) == -1) { 155 fprintf(stderr, "invalid packet received. retrying\n"); 156 continue; 157 } 158 159 n = get_udp_data(&data, raw); 160 if ((size_t)n > sizeof(*dhcp)) { 161 fprintf(stderr, "invalid packet size. retrying\n"); 162 continue; 163 } 164 dhcp = (const void *)data; 165 166 /* XXX: what if packet is too small? */ 167 168 /* some sanity checks */ 169 if (dhcp->cookie != htonl(MAGIC_COOKIE)) { 170 /* ignore */ 171 continue; 172 } 173 174 if (ifp->state->xid != dhcp->xid) { 175 fprintf(stderr, "invalid transaction. retrying\n"); 176 continue; 177 } 178 179 break; 180 } 181 182 *rawp = raw; 183 *dhcpp = dhcp; 184 } 185 186 static void 187 get_offer(struct interface *ifp) 188 { 189 const struct dhcp_message *dhcp; 190 uint8_t *raw; 191 uint8_t type; 192 193 get_network(ifp, &raw, &dhcp); 194 195 get_option_uint8(&type, dhcp, DHO_MESSAGETYPE); 196 switch (type) { 197 case DHCP_OFFER: 198 break; 199 case DHCP_NAK: 200 errx(EXIT_FAILURE, "got NAK from dhcp server"); 201 default: 202 errx(EXIT_FAILURE, "didn't receive offer"); 203 } 204 205 ifp->state->offer = xzalloc(sizeof(*ifp->state->offer)); 206 memcpy(ifp->state->offer, dhcp, sizeof(*ifp->state->offer)); 207 ifp->state->lease.addr.s_addr = dhcp->yiaddr; 208 ifp->state->lease.cookie = dhcp->cookie; 209 free(raw); 210 } 211 212 static void 213 get_ack(struct interface *ifp) 214 { 215 const struct dhcp_message *dhcp; 216 uint8_t *raw; 217 uint8_t type; 218 219 get_network(ifp, &raw, &dhcp); 220 get_option_uint8(&type, dhcp, DHO_MESSAGETYPE); 221 if (type != DHCP_ACK) 222 errx(EXIT_FAILURE, "didn't receive ack"); 223 224 ifp->state->new = ifp->state->offer; 225 get_lease(&ifp->state->lease, ifp->state->new); 226 } 227 228 int 229 main(int argc, char *argv[]) 230 { 231 struct interface *ifp; 232 struct if_options *ifo; 233 const int mib[] = { CTL_KERN, KERN_HOSTNAME }; 234 size_t hlen; 235 236 setprogname(argv[0]); 237 if (argc != 2) 238 usage(); 239 240 if (rumpclient_init() == -1) 241 err(EXIT_FAILURE, "init failed"); 242 243 if (init_sockets() == -1) 244 err(EXIT_FAILURE, "failed to init sockets"); 245 if ((ifp = init_interface(argv[1])) == NULL) 246 err(EXIT_FAILURE, "cannot init %s", argv[1]); 247 ifaces = ifp; 248 if (open_socket(ifp, ETHERTYPE_IP) == -1) 249 err(EXIT_FAILURE, "bpf"); 250 up_interface(ifp); 251 252 ifp->state = xzalloc(sizeof(*ifp->state)); 253 ifp->state->options = ifo = xzalloc(sizeof(*ifp->state->options)); 254 ifp->state->xid = arc4random(); 255 256 hlen = sizeof(ifo->hostname); 257 if (rump_sys___sysctl(mib, __arraycount(mib), ifo->hostname, &hlen, 258 NULL, 0) == -1) 259 snprintf(ifo->hostname, sizeof(ifo->hostname), 260 "unknown.hostname"); 261 ifo->options = DHCPCD_GATEWAY | DHCPCD_HOSTNAME; 262 263 if (get_hwaddr(ifp) == -1) 264 err(EXIT_FAILURE, "failed to get hwaddr for %s", ifp->name); 265 266 send_discover(ifp); 267 get_offer(ifp); 268 send_request(ifp); 269 get_ack(ifp); 270 271 configure(ifp); 272 273 return 0; 274 } 275