]> git.saurik.com Git - apple/xnu.git/blame - bsd/netinet/in_bootp.c
xnu-123.5.tar.gz
[apple/xnu.git] / bsd / netinet / in_bootp.c
CommitLineData
1c79356b
A
1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22/*
23 * Copyright (c) 1988-1999 Apple Computer, Inc. All Rights Reserved
24 */
25
26/*
27 * bootp.c
28 * - be a BOOTP client over a particular interface to retrieve
29 * the IP address, netmask, and router
30 */
31
32/*
33 * Modification History
34 *
35 * February 19, 1999 Dieter Siegmund (dieter@apple.com)
36 * - completely rewritten
37 */
38
39#include <sys/param.h>
40#include <sys/types.h>
41#include <mach/boolean.h>
42#include <sys/kernel.h>
43#include <sys/errno.h>
44#include <sys/file.h>
45#include <sys/uio.h>
46#include <sys/ioctl.h>
47#include <sys/time.h>
48#include <sys/mbuf.h>
49#include <sys/vnode.h>
50#include <sys/socket.h>
51#include <sys/socketvar.h>
52#include <net/if.h>
53#include <net/if_dl.h>
54#include <net/if_types.h>
55#include <net/route.h>
56#include <netinet/in.h>
57#include <netinet/in_systm.h>
58#include <netinet/if_ether.h>
59#include <netinet/ip.h>
60#include <netinet/ip_var.h>
61#include <netinet/udp.h>
62#include <netinet/udp_var.h>
63#include <netinet/ip_icmp.h>
64#include <netinet/bootp.h>
65#include <sys/systm.h>
66#include <sys/malloc.h>
67
68
69#ifdef BOOTP_DEBUG
70#define dprintf(x) printf x;
71#else BOOTP_DEBUG
72#define dprintf(x)
73#endif BOOTP_DEBUG
74
75/* ip address formatting macros */
76#define IP_FORMAT "%d.%d.%d.%d"
77#define IP_CH(ip) ((u_char *)ip)
78#define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
79
80/* tag values (from RFC 2132) */
81#define TAG_PAD 0
82#define TAG_END 255
83#define TAG_SUBNET_MASK 1
84#define TAG_ROUTER 3
85#define RFC_OPTIONS_MAGIC { 99, 130, 83, 99 }
86static unsigned char rfc_magic[4] = RFC_OPTIONS_MAGIC;
87
88
89static struct sockaddr_in blank_sin = { sizeof(struct sockaddr_in),
90 AF_INET };
91
92static __inline__ void
93print_reply(struct bootp *bp, int bp_len)
94{
95 int i, j, len;
96
97 printf("bp_op = ");
98 if (bp->bp_op == BOOTREQUEST) printf("BOOTREQUEST\n");
99 else if (bp->bp_op == BOOTREPLY) printf("BOOTREPLY\n");
100 else
101 {
102 i = bp->bp_op;
103 printf("%d\n", i);
104 }
105
106 i = bp->bp_htype;
107 printf("bp_htype = %d\n", i);
108
109 len = bp->bp_hlen;
110 printf("bp_hlen = %d\n", len);
111
112 i = bp->bp_hops;
113 printf("bp_hops = %d\n", i);
114
115 printf("bp_xid = %lu\n", bp->bp_xid);
116
117 printf("bp_secs = %u\n", bp->bp_secs);
118
119 printf("bp_ciaddr = " IP_FORMAT "\n", IP_LIST(&bp->bp_ciaddr));
120 printf("bp_yiaddr = " IP_FORMAT "\n", IP_LIST(&bp->bp_yiaddr));
121 printf("bp_siaddr = " IP_FORMAT "\n", IP_LIST(&bp->bp_siaddr));
122 printf("bp_giaddr = " IP_FORMAT "\n", IP_LIST(&bp->bp_giaddr));
123
124 printf("bp_chaddr = ");
125 for (j = 0; j < len; j++)
126 {
127 i = bp->bp_chaddr[j];
128 printf("%0x", i);
129 if (j < (len - 1)) printf(":");
130 }
131 printf("\n");
132
133 printf("bp_sname = %s\n", bp->bp_sname);
134 printf("bp_file = %s\n", bp->bp_file);
135}
136
137static __inline__ void
138print_reply_short(struct bootp *bp, int bp_len)
139{
140 printf("bp_yiaddr = " IP_FORMAT "\n", IP_LIST(&bp->bp_yiaddr));
141 printf("bp_sname = %s\n", bp->bp_sname);
142}
143
144
145static __inline__ long
146random_range(long bottom, long top)
147{
148 long number = top - bottom + 1;
149 long range_size = LONG_MAX / number;
150 return (((long)random()) / range_size + bottom);
151}
152
153/*
154 * Function: make_bootp_request
155 * Purpose:
156 * Create a "blank" bootp packet.
157 */
158static void
159make_bootp_request(struct bootp_packet * pkt,
160 u_char * hwaddr, u_char hwtype, u_char hwlen)
161{
162 bzero(pkt, sizeof (*pkt));
163 pkt->bp_ip.ip_v = IPVERSION;
164 pkt->bp_ip.ip_hl = sizeof (struct ip) >> 2;
165 pkt->bp_ip.ip_id = htons(ip_id++);
166 pkt->bp_ip.ip_ttl = MAXTTL;
167 pkt->bp_ip.ip_p = IPPROTO_UDP;
168 pkt->bp_ip.ip_src.s_addr = 0;
169 pkt->bp_ip.ip_dst.s_addr = htonl(INADDR_BROADCAST);
170 pkt->bp_udp.uh_sport = htons(IPPORT_BOOTPC);
171 pkt->bp_udp.uh_dport = htons(IPPORT_BOOTPS);
172 pkt->bp_udp.uh_sum = 0;
173 pkt->bp_bootp.bp_op = BOOTREQUEST;
174 pkt->bp_bootp.bp_htype = hwtype;
175 pkt->bp_bootp.bp_hlen = hwlen;
176 pkt->bp_bootp.bp_ciaddr.s_addr = 0;
177 bcopy(hwaddr, pkt->bp_bootp.bp_chaddr, hwlen);
178 bcopy(rfc_magic, pkt->bp_bootp.bp_vend, sizeof(rfc_magic));
179 pkt->bp_bootp.bp_vend[4] = TAG_END;
180 pkt->bp_udp.uh_ulen = htons(sizeof(pkt->bp_udp) + sizeof(pkt->bp_bootp));
181 pkt->bp_ip.ip_len = htons(sizeof(struct ip) + ntohs(pkt->bp_udp.uh_ulen));
182 pkt->bp_ip.ip_sum = 0;
183 return;
184}
185
186/*
187 * Function: ip_pkt_to_mbuf
188 * Purpose:
189 * Put the given IP packet into an mbuf, calculate the
190 * IP checksum.
191 */
192struct mbuf *
193ip_pkt_to_mbuf(caddr_t pkt, int pktsize)
194{
195 struct ip * ip;
196 struct mbuf * m;
197
198 m = (struct mbuf *)m_devget(pkt, pktsize, 0, 0, 0);
199 if (m == 0) {
200 printf("bootp: ip_pkt_to_mbuf: m_devget failed\n");
201 return 0;
202 }
203 m->m_flags |= M_BCAST;
204 /* Compute the checksum */
205 ip = mtod(m, struct ip *);
206 ip->ip_sum = 0;
207 ip->ip_sum = in_cksum(m, sizeof (struct ip));
208 return (m);
209}
210
211static __inline__ u_char *
212link_address(struct sockaddr_dl * dl_p)
213{
214 return (dl_p->sdl_data + dl_p->sdl_nlen);
215}
216
217static __inline__ void
218link_print(struct sockaddr_dl * dl_p)
219{
220 int i;
221
222#if 0
223 printf("len %d index %d family %d type 0x%x nlen %d alen %d"
224 " slen %d addr ", dl_p->sdl_len,
225 dl_p->sdl_index, dl_p->sdl_family, dl_p->sdl_type,
226 dl_p->sdl_nlen, dl_p->sdl_alen, dl_p->sdl_slen);
227#endif 0
228 for (i = 0; i < dl_p->sdl_alen; i++)
229 printf("%s%x", i ? ":" : "",
230 (link_address(dl_p))[i]);
231 printf("\n");
232 return;
233}
234
235static struct sockaddr_dl *
236link_from_ifnet(struct ifnet * ifp)
237{
238 struct ifaddr * addr;
239
240/* for (addr = ifp->if_addrlist; addr; addr = addr->ifa_next) */
241
242 TAILQ_FOREACH(addr, &ifp->if_addrhead, ifa_link) {
243 if (addr->ifa_addr->sa_family == AF_LINK) {
244 struct sockaddr_dl * dl_p = (struct sockaddr_dl *)(addr->ifa_addr);
245
246 return (dl_p);
247 }
248 }
249 return (NULL);
250}
251
252/*
253 * Function: send_bootp_request
254 * Purpose:
255 * Send the request by calling the interface's output routine
256 * bypassing routing code.
257 */
258static int
259send_bootp_request(struct ifnet * ifp, struct socket * so,
260 struct bootp_packet * pkt)
261{
262 struct mbuf * m;
263 struct sockaddr_in sin;
264
265 /* Address to send to */
266 sin = blank_sin;
267 sin.sin_port = htons(IPPORT_BOOTPS);
268 sin.sin_addr.s_addr = INADDR_BROADCAST;
269
270 m = ip_pkt_to_mbuf((caddr_t)pkt, sizeof(*pkt));
271 return (dlil_output((u_long) ifp, m, 0, (struct sockaddr *)&sin, 0));
272}
273
274/*
275 * Function: receive_packet
276 * Purpose:
277 * Return a received packet or an error if none available.
278 */
279int
280receive_packet(struct socket * so, caddr_t pp, int psize)
281{
282 struct iovec aiov;
283 struct uio auio;
284 int rcvflg;
285 int error;
286
287 aiov.iov_base = pp;
288 aiov.iov_len = psize;
289 auio.uio_iov = &aiov;
290 auio.uio_iovcnt = 1;
291 auio.uio_segflg = UIO_SYSSPACE;
292 auio.uio_offset = 0;
293 auio.uio_resid = psize;
294 auio.uio_rw = UIO_READ;
295 rcvflg = MSG_WAITALL;
296
297 error = soreceive(so, (struct sockaddr **) 0, &auio, 0, 0, &rcvflg);
298 return (error);
299}
300
301/*
302 * Function: bootp_timeout
303 * Purpose:
304 * Wakeup the process waiting for something on a socket.
305 */
306static void
307bootp_timeout(struct socket * * socketflag)
308{
309 struct socket * so = *socketflag;
310 boolean_t funnel_state;
311
312 dprintf(("bootp: timeout\n"));
313
314 funnel_state = thread_funnel_set(network_flock,TRUE);
315 *socketflag = NULL;
316 sowakeup(so, &so->so_rcv);
317 (void) thread_funnel_set(network_flock, FALSE);
318 return;
319}
320
321#define TAG_OFFSET 0
322#define LEN_OFFSET 1
323#define OPTION_OFFSET 2
324
325void *
326packet_option(struct bootp * pkt, u_char t)
327{
328 void * buffer = pkt->bp_vend + sizeof(rfc_magic);
329 int len;
330 unsigned char option_len;
331 void * ret = NULL;
332 unsigned char * scan;
333 unsigned char tag = TAG_PAD;
334
335 len = sizeof(pkt->bp_vend) - sizeof(rfc_magic);
336 for (scan = buffer; len > 0; ) {
337 tag = scan[TAG_OFFSET];
338 if (tag == TAG_END) /* we hit the end of the options */
339 break;
340 if (tag == TAG_PAD) { /* discard pad characters */
341 scan++;
342 len--;
343 }
344 else {
345 if (t == tag && ret == NULL)
346 ret = scan + OPTION_OFFSET;
347 option_len = scan[LEN_OFFSET];
348 len -= (option_len + 2);
349 scan += (option_len + 2);
350 }
351 }
352 if (len < 0 || tag != TAG_END) { /* we ran off the end */
353 if (len < 0) {
354 dprintf(("bootp: error parsing options\n"));
355 }
356 else {
357 dprintf(("bootp: end tag missing\n"));
358 }
359 ret = NULL;
360 }
361 return (ret);
362}
363
364/*
365 * Function: rate_packet
366 * Purpose:
367 * Return an integer point rating value for the given bootp packet.
368 * If yiaddr non-zero, the packet gets a rating of 1.
369 * Another point is given if the packet contains the subnet mask,
370 * and another if the router is present.
371 */
372#define GOOD_RATING 3
373static __inline__ int
374rate_packet(struct bootp * pkt)
375{
376 int rating = 0;
377
378 if (pkt->bp_yiaddr.s_addr) {
379 struct in_addr * ip;
380
381 rating++;
382 ip = (struct in_addr *)packet_option(pkt, TAG_SUBNET_MASK);
383 if (ip)
384 rating++;
385 ip = (struct in_addr *)packet_option(pkt, TAG_ROUTER);
386 if (ip)
387 rating++;
388 }
389 return (rating);
390}
391
392#define INITIAL_WAIT_SECS 4
393#define MAX_WAIT_SECS 64
394#define GATHER_TIME_SECS 2
395#define RAND_TICKS (hz) /* one second */
396
397/*
398 * Function: bootp_loop
399 * Purpose:
400 * Do the actual BOOTP protocol.
401 * The algorithm sends out a packet, waits for a response.
402 * We try max_try times, waiting in an exponentially increasing
403 * amount of time. Once we receive a good response, we start
404 * a new time period called the "gather time", during which we
405 * either find the perfect packet (one that has ip, mask and router)
406 * or we continue to gather responses. At the end of the gather period,
407 * we use the best response gathered.
408 */
409static int
410bootp_loop(struct socket * so, struct ifnet * ifp, int max_try,
411 struct in_addr * iaddr_p, struct in_addr * netmask_p,
412 struct in_addr * router_p)
413{
414 struct timeval current_time;
415 struct sockaddr_dl * dl_p;
416 int error = 0;
417 char * hwaddr;
418 int hwlen;
419 char hwtype = 0;
420 struct bootp_packet * request = NULL;
421 struct bootp * reply = NULL;
422 struct bootp * saved_reply = NULL;
423 struct timeval start_time;
424 u_long xid;
425 int retry;
426 struct socket * timeflag;
427 int wait_ticks = INITIAL_WAIT_SECS * hz;
428
429 /* get the hardware address from the interface */
430 dl_p = link_from_ifnet(ifp);
431 if (dl_p == NULL) {
432 printf("bootp: can't get link address\n");
433 return (ENXIO);
434 }
435
436 printf("bootp: h/w addr ");
437 link_print(dl_p);
438
439 hwaddr = link_address(dl_p);
440 hwlen = dl_p->sdl_alen;
441 switch (dl_p->sdl_type) {
442 case IFT_ETHER:
443 hwtype = ARPHRD_ETHER;
444 break;
445 default:
446 printf("bootp: hardware type %d not supported\n",
447 dl_p->sdl_type);
448 panic("bootp: hardware type not supported");
449 break;
450 }
451
452 /* set transaction id and remember the start time */
453 microtime(&start_time);
454 current_time = start_time;
455 xid = random();
456
457 /* make a request/reply packet */
458 request = (struct bootp_packet *)kalloc(sizeof(*request));
459 make_bootp_request(request, hwaddr, hwtype, hwlen);
460 reply = (struct bootp *)kalloc(sizeof(*reply));
461 saved_reply = (struct bootp *)kalloc(sizeof(*saved_reply));
462 iaddr_p->s_addr = 0;
463 printf("bootp: sending request");
464 for (retry = 0; retry < max_try; retry++) {
465 int gather_count = 0;
466 int last_rating = 0;
467
468 /* Send the request */
469 printf(".");
470 request->bp_bootp.bp_secs = htons((u_short)(current_time.tv_sec
471 - start_time.tv_sec));
472 request->bp_bootp.bp_xid = htonl(xid);
473 error = send_bootp_request(ifp, so, request);
474 if (error)
475 goto cleanup;
476
477 timeflag = so;
478 wait_ticks += random_range(-RAND_TICKS, RAND_TICKS);
479 dprintf(("bootp: waiting %d ticks\n", wait_ticks));
480 timeout(bootp_timeout, &timeflag, wait_ticks);
481
482 while (TRUE) {
483 error = receive_packet(so, (caddr_t)reply, sizeof(*reply));
484 if (error == 0) {
485 dprintf(("\nbootp: received packet\n"));
486 if (ntohl(reply->bp_xid) == xid
487 && reply->bp_yiaddr.s_addr
488 && bcmp(reply->bp_chaddr, hwaddr, hwlen) == 0) {
489 int rating;
490#ifdef BOOTP_DEBUG
491 print_reply_short(reply, sizeof(*reply));
492#endif BOOTP_DEBUG
493 rating = rate_packet(reply);
494 if (rating > last_rating)
495 *saved_reply = *reply;
496 if (rating >= GOOD_RATING) {
497 untimeout(bootp_timeout, &timeflag);
498 goto save_values;
499 }
500 if (gather_count == 0) {
501 untimeout(bootp_timeout, &timeflag);
502 timeflag = so;
503 timeout(bootp_timeout, &timeflag,
504 hz * GATHER_TIME_SECS);
505 }
506 gather_count++;
507 }
508 else {
509 dprintf(("bootp: packet ignored\n"));
510 }
511 }
512 else if ((error != EWOULDBLOCK)) {
513 break;
514 }
515 else if (timeflag == NULL) { /* timed out */
516 if (gather_count) {
517 dprintf(("bootp: gathering time has expired"));
518 goto save_values; /* we have a packet */
519 }
520 break; /* retry */
521 }
522 else
523 sbwait(&so->so_rcv);
524 }
525 if (error && (error != EWOULDBLOCK)) {
526 dprintf(("bootp: failed to receive packets: %d\n", error));
527 untimeout(bootp_timeout, &timeflag);
528 goto cleanup;
529 }
530 wait_ticks *= 2;
531 if (wait_ticks > (MAX_WAIT_SECS * hz))
532 wait_ticks = MAX_WAIT_SECS * hz;
533 xid++;
534 microtime(&current_time);
535 }
536 error = ETIMEDOUT;
537 goto cleanup;
538
539 save_values:
540 error = 0;
541 printf("\nbootp: got response from %s (" IP_FORMAT ")\n",
542 saved_reply->bp_sname, IP_LIST(&saved_reply->bp_siaddr));
543 /* return the ip address */
544 *iaddr_p = saved_reply->bp_yiaddr;
545 {
546 struct in_addr * ip;
547 ip = (struct in_addr *)packet_option(saved_reply, TAG_SUBNET_MASK);
548 if (ip)
549 *netmask_p = *ip;
550 ip = (struct in_addr *)packet_option(saved_reply, TAG_ROUTER);
551 if (ip)
552 *router_p = *ip;
553 }
554
555 cleanup:
556 if (request)
557 kfree((caddr_t)request, sizeof (*request));
558 if (reply)
559 kfree((caddr_t)reply, sizeof(*reply));
560 if (saved_reply)
561 kfree((caddr_t)saved_reply, sizeof(*saved_reply));
562 return (error);
563}
564
565/*
566 * Routine: bootp
567 * Function:
568 * Use the BOOTP protocol to resolve what our IP address should be
569 * on a particular interface.
570 */
571int bootp(struct ifnet * ifp, struct in_addr * iaddr_p, int max_try,
572 struct in_addr * netmask_p, struct in_addr * router_p,
573 struct proc * procp)
574{
575 boolean_t addr_set = FALSE;
576 struct ifreq ifr;
577 int error;
578 struct socket * so = NULL;
579
580 /* get a socket */
581 error = socreate(AF_INET, &so, SOCK_DGRAM, 0);
582 if (error) {
583 dprintf(("bootp: socreate failed %d\n", error));
584 return (error);
585 }
586
587 /* assign the all-zeroes address */
588 bzero(&ifr, sizeof(ifr));
589 sprintf(ifr.ifr_name, "%s%d", ifp->if_name, ifp->if_unit);
590 *((struct sockaddr_in *)&ifr.ifr_addr) = blank_sin;
591 error = ifioctl(so, SIOCSIFADDR, (caddr_t)&ifr, procp);
592 if (error) {
593 dprintf(("bootp: SIOCSIFADDR all-zeroes IP failed: %d\n",
594 error));
595 goto cleanup;
596 }
597 dprintf(("bootp: all-zeroes IP address assigned\n"));
598 addr_set = TRUE;
599
600 { /* bind the socket */
601 struct sockaddr_in * sin;
602
603 sin = _MALLOC(sizeof(struct sockaddr_in), M_IFADDR, M_NOWAIT);
604 sin->sin_len = sizeof(struct sockaddr_in);
605 sin->sin_family = AF_INET;
606 sin->sin_port = htons(IPPORT_BOOTPC);
607 sin->sin_addr.s_addr = INADDR_ANY;
608 error = sobind(so, (struct sockaddr *) sin);
609
610 FREE(sin, M_IFADDR);
611 if (error) {
612 dprintf(("bootp: sobind failed, %d\n", error));
613 goto cleanup;
614 }
615 so->so_state |= SS_NBIO;
616 }
617 /* do the protocol */
618 error = bootp_loop(so, ifp, max_try, iaddr_p, netmask_p, router_p);
619
620 cleanup:
621 if (so) {
622 if (addr_set) {
623 (void) ifioctl(so, SIOCDIFADDR, (caddr_t) &ifr, procp);
624 }
625 soclose(so);
626 }
627 return (error);
628}
629
630/*
631 * Function: in_bootp
632 * Purpose:
633 * This is deprecated API. Once SIOCAUTOADDR is eliminated from
634 * the system (IOEthernet class as well), this routine can be removed.
635 */
636int
637in_bootp(struct ifnet * ifp, struct sockaddr_in * sin, u_char my_enaddr[6])
638{
639 return (EOPNOTSUPP);
640}