1 /* $NetBSD: main.c,v 1.1 2011/01/20 18:47:20 pooka 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 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(1, "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(1, "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(1, "timed out waiting for response"); 144 case -1: 145 err(1, "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(1, "got NAK from dhcp server"); 201 default: 202 errx(1, "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 free(raw); 208 } 209 210 static void 211 get_ack(struct interface *ifp) 212 { 213 const struct dhcp_message *dhcp; 214 uint8_t *raw; 215 uint8_t type; 216 217 get_network(ifp, &raw, &dhcp); 218 get_option_uint8(&type, dhcp, DHO_MESSAGETYPE); 219 if (type != DHCP_ACK) 220 errx(1, "didn't receive ack"); 221 222 ifp->state->new = ifp->state->offer; 223 get_lease(&ifp->state->lease, ifp->state->new); 224 } 225 226 int 227 main(int argc, char *argv[]) 228 { 229 struct interface *ifp; 230 struct if_options *ifo; 231 const int mib[] = { CTL_KERN, KERN_HOSTNAME }; 232 size_t hlen; 233 234 setprogname(argv[0]); 235 if (argc != 2) 236 usage(); 237 238 if (rumpclient_init() == -1) 239 err(1, "init failed"); 240 241 if (init_sockets() == -1) 242 err(1, "failed to init sockets"); 243 if ((ifp = init_interface(argv[1])) == NULL) 244 err(1, "cannot init %s\n", argv[1]); 245 ifaces = ifp; 246 if (open_socket(ifp, ETHERTYPE_IP) == -1) 247 err(1, "bpf"); 248 up_interface(ifp); 249 250 ifp->state = xzalloc(sizeof(*ifp->state)); 251 ifp->state->options = ifo = xzalloc(sizeof(*ifp->state->options)); 252 ifp->state->xid = arc4random(); 253 254 hlen = sizeof(ifo->hostname); 255 if (rump_sys___sysctl(mib, __arraycount(mib), ifo->hostname, &hlen, 256 NULL, 0) == -1) 257 snprintf(ifo->hostname, sizeof(ifo->hostname), 258 "unknown.hostname"); 259 ifo->options = DHCPCD_GATEWAY | DHCPCD_HOSTNAME; 260 261 if (get_hwaddr(ifp) == -1) 262 err(1, "failed to get hwaddr for %s", ifp->name); 263 264 send_discover(ifp); 265 get_offer(ifp); 266 send_request(ifp); 267 get_ack(ifp); 268 269 configure(ifp); 270 271 return 0; 272 } 273