#include <sys/param.h>
#include <sys/types.h>
#include <mach/boolean.h>
#include <sys/kernel.h>
#include <sys/errno.h>
#include <sys/file.h>
#include <sys/uio.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/mbuf.h>
#include <sys/vnode.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/uio_internal.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/route.h>
#include <net/dlil.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/udp.h>
#include <netinet/udp_var.h>
#include <netinet/ip_icmp.h>
#include <netinet/bootp.h>
#include <netinet/dhcp.h>
#include <netinet/in_dhcp.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <netinet/dhcp_options.h>
#include <kern/kern_types.h>
#include <kern/kalloc.h>
#ifdef DHCP_DEBUG
#define dprintf(x) printf x;
#else
#define dprintf(x)
#endif
#define INITIAL_WAIT_SECS 2
#define MAX_WAIT_SECS 64
#define GATHER_TIME_SECS 4
#define RAND_TICKS (hz)
const struct sockaddr_in blank_sin = {
sizeof(struct sockaddr_in),
AF_INET,
0,
{ 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0 }
};
__private_extern__ int
inet_aifaddr(struct socket * so, const char * name,
const struct in_addr * addr,
const struct in_addr * mask,
const struct in_addr * broadcast)
{
struct ifaliasreq ifra;
bzero(&ifra, sizeof(ifra));
strlcpy(ifra.ifra_name, name, sizeof(ifra.ifra_name));
if (addr) {
*((struct sockaddr_in *)&ifra.ifra_addr) = blank_sin;
((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr = *addr;
}
if (mask) {
*((struct sockaddr_in *)&ifra.ifra_mask) = blank_sin;
((struct sockaddr_in *)&ifra.ifra_mask)->sin_addr = *mask;
}
if (broadcast) {
*((struct sockaddr_in *)&ifra.ifra_broadaddr) = blank_sin;
((struct sockaddr_in *)&ifra.ifra_broadaddr)->sin_addr = *broadcast;
}
return (ifioctl(so, SIOCAIFADDR, (caddr_t)&ifra, current_proc()));
}
struct dhcp_context {
struct ifnet * ifp;
struct sockaddr_dl * dl_p;
struct ifreq ifr;
struct socket * so;
uint8_t request[DHCP_PACKET_MIN];
dhcpoa_t request_options;
uint8_t reply[DHCP_PAYLOAD_MIN];
struct timeval start_time;
uint32_t xid;
int max_try;
struct in_addr iaddr;
struct in_addr netmask;
struct in_addr router;
struct in_addr server_id;
};
static __inline__ struct dhcp_packet *
dhcp_context_request(struct dhcp_context * context)
{
return ((struct dhcp_packet *)context->request);
}
static __inline__ struct dhcp *
dhcp_context_reply(struct dhcp_context * context)
{
return ((struct dhcp *)context->reply);
}
struct mbuf * ip_pkt_to_mbuf(caddr_t pkt, int pktsize);
static int
receive_packet(struct socket * so, void * pp, int psize,
int * actual_size);
#define IP_FORMAT "%d.%d.%d.%d"
#define IP_CH(ip) ((const uint8_t *)ip)
#define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
#define SUGGESTED_LEASE_LENGTH (60 * 60 * 24 * 30 * 3)
static const uint8_t dhcp_params[] = {
dhcptag_subnet_mask_e,
dhcptag_router_e,
};
#define N_DHCP_PARAMS (sizeof(dhcp_params) / sizeof(dhcp_params[0]))
static __inline__ long
random_range(long bottom, long top)
{
long number = top - bottom + 1;
long range_size = LONG_MAX / number;
return (((long)random()) / range_size + bottom);
}
static void
init_dhcp_packet_header(struct dhcp_packet * pkt, int pkt_size)
{
bzero(&pkt->ip, sizeof(pkt->ip));
bzero(&pkt->udp, sizeof(pkt->udp));
pkt->ip.ip_v = IPVERSION;
pkt->ip.ip_hl = sizeof(struct ip) >> 2;
pkt->ip.ip_ttl = MAXTTL;
pkt->ip.ip_p = IPPROTO_UDP;
pkt->ip.ip_src.s_addr = 0;
pkt->ip.ip_dst.s_addr = htonl(INADDR_BROADCAST);
pkt->ip.ip_len = htons(pkt_size);
pkt->ip.ip_sum = 0;
pkt->udp.uh_sport = htons(IPPORT_BOOTPC);
pkt->udp.uh_dport = htons(IPPORT_BOOTPS);
pkt->udp.uh_sum = 0;
pkt->udp.uh_ulen = htons(pkt_size - sizeof(pkt->ip));
return;
}
static void
make_dhcp_request(struct dhcp * request, int request_size,
dhcp_msgtype_t msg,
const uint8_t * hwaddr, uint8_t hwtype, int hwlen,
dhcpoa_t * options_p)
{
uint8_t cid[ETHER_ADDR_LEN + 1];
uint8_t rfc_magic[RFC_MAGIC_SIZE] = RFC_OPTIONS_MAGIC;
if (hwlen > (int)sizeof(cid)) {
printf("dhcp: hwlen is %d (> %d), truncating\n", hwlen,
(int)sizeof(cid));
hwlen = sizeof(cid);
}
bzero(request, request_size);
request->dp_op = BOOTREQUEST;
request->dp_htype = hwtype;
request->dp_hlen = hwlen;
bcopy(hwaddr, request->dp_chaddr, hwlen);
bcopy(rfc_magic, request->dp_options, RFC_MAGIC_SIZE);
dhcpoa_init(options_p, request->dp_options + RFC_MAGIC_SIZE,
request_size - sizeof(struct dhcp) - RFC_MAGIC_SIZE);
dhcpoa_add_dhcpmsg(options_p, msg);
dhcpoa_add(options_p, dhcptag_parameter_request_list_e,
N_DHCP_PARAMS, dhcp_params);
cid[0] = hwtype;
bcopy(hwaddr, cid + 1, hwlen);
dhcpoa_add(options_p, dhcptag_client_identifier_e, hwlen + 1, cid);
return;
}
struct mbuf *
ip_pkt_to_mbuf(caddr_t pkt, int pktsize)
{
struct ip * ip;
struct mbuf * m;
m = (struct mbuf *)m_devget(pkt, pktsize, 0, NULL, NULL);
if (m == 0) {
printf("dhcp: ip_pkt_to_mbuf: m_devget failed\n");
return NULL;
}
m->m_flags |= M_BCAST;
ip = mtod(m, struct ip *);
ip->ip_sum = 0;
ip->ip_sum = in_cksum(m, sizeof(struct ip));
return (m);
}
static __inline__ u_char *
link_address(struct sockaddr_dl * dl_p)
{
return (u_char *)(dl_p->sdl_data + dl_p->sdl_nlen);
}
static __inline__ int
link_address_length(struct sockaddr_dl * dl_p)
{
return (dl_p->sdl_alen);
}
static __inline__ void
link_print(struct sockaddr_dl * dl_p)
{
int i;
#if 0
printf("len %d index %d family %d type 0x%x nlen %d alen %d"
" slen %d addr ", dl_p->sdl_len,
dl_p->sdl_index, dl_p->sdl_family, dl_p->sdl_type,
dl_p->sdl_nlen, dl_p->sdl_alen, dl_p->sdl_slen);
#endif
for (i = 0; i < dl_p->sdl_alen; i++)
printf("%s%x", i ? ":" : "",
(link_address(dl_p))[i]);
printf("\n");
return;
}
static struct sockaddr_dl *
link_from_ifnet(struct ifnet * ifp)
{
struct ifaddr * addr;
ifnet_lock_shared(ifp);
TAILQ_FOREACH(addr, &ifp->if_addrhead, ifa_link) {
if (addr->ifa_addr->sa_family == AF_LINK) {
struct sockaddr_dl * dl_p = (struct sockaddr_dl *)(addr->ifa_addr);
ifnet_lock_done(ifp);
return (dl_p);
}
}
ifnet_lock_done(ifp);
return (NULL);
}
static int
send_packet(struct ifnet * ifp, struct dhcp_packet * pkt, int pkt_size)
{
struct mbuf * m;
struct sockaddr_in dest;
dest = blank_sin;
dest.sin_port = htons(IPPORT_BOOTPS);
dest.sin_addr.s_addr = INADDR_BROADCAST;
m = ip_pkt_to_mbuf((caddr_t)pkt, pkt_size);
return dlil_output(ifp, PF_INET, m, 0, (struct sockaddr *)&dest, 0);
}
static int
receive_packet(struct socket * so, void * pp, int psize, int * actual_size)
{
uio_t auio;
int error;
int rcvflg;
char uio_buf[ UIO_SIZEOF(1) ];
auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
&uio_buf[0], sizeof(uio_buf));
uio_addiov(auio, CAST_USER_ADDR_T(pp), psize);
rcvflg = MSG_WAITALL;
error = soreceive(so, (struct sockaddr **) 0, auio, 0, 0, &rcvflg);
*actual_size = psize - uio_resid(auio);
return (error);
}
static void
dhcp_timeout(void * arg)
{
struct socket * * timer_arg = (struct socket * *)arg;
struct socket * so = *timer_arg;
dprintf(("dhcp: timeout\n"));
*timer_arg = NULL;
socket_lock(so, 1);
sowakeup(so, &so->so_rcv);
socket_unlock(so, 1);
return;
}
#define GOOD_RATING 3
static __inline__ int
rate_packet(dhcpol_t * options_p)
{
int len;
int rating = 1;
if (dhcpol_find(options_p, dhcptag_subnet_mask_e, &len, NULL) != NULL) {
rating++;
}
if (dhcpol_find(options_p, dhcptag_router_e, &len, NULL) != NULL) {
rating++;
}
return (rating);
}
static dhcp_msgtype_t
get_dhcp_msgtype(dhcpol_t * options_p)
{
int len;
const uint8_t * opt;
opt = dhcpol_find(options_p, dhcptag_dhcp_message_type_e, &len, NULL);
if (opt != NULL && len == 1) {
return (*opt);
}
return (dhcp_msgtype_none_e);
}
static int
dhcp_get_ack(struct dhcp_context * context, int wait_ticks)
{
int error = 0;
const struct in_addr * ip;
int len;
int n;
struct dhcp * reply;
struct in_addr server_id;
struct socket * timer_arg;
timer_arg = context->so;
reply = dhcp_context_reply(context);
timeout((timeout_fcn_t)dhcp_timeout, &timer_arg, wait_ticks);
while (1) {
error = receive_packet(context->so, context->reply,
sizeof(context->reply), &n);
if (error == 0) {
dhcp_msgtype_t msg;
dhcpol_t options;
dprintf(("\ndhcp: received packet length %d\n", n));
if (n < (int)sizeof(struct dhcp)) {
dprintf(("dhcp: packet is too short %d < %d\n",
n, (int)sizeof(struct dhcp)));
continue;
}
if (ntohl(reply->dp_xid) != context->xid
|| bcmp(reply->dp_chaddr, link_address(context->dl_p),
link_address_length(context->dl_p)) != 0) {
continue;
}
(void)dhcpol_parse_packet(&options, reply, n);
server_id.s_addr = 0;
ip = (const struct in_addr *)
dhcpol_find(&options,
dhcptag_server_identifier_e, &len, NULL);
if (ip != NULL && len >= (int)sizeof(*ip)) {
server_id = *ip;
}
msg = get_dhcp_msgtype(&options);
if (msg == dhcp_msgtype_nak_e
&& server_id.s_addr == context->server_id.s_addr) {
dhcpol_free(&options);
error = EPROTO;
untimeout((timeout_fcn_t)dhcp_timeout, &timer_arg);
break;
}
if (msg != dhcp_msgtype_ack_e
|| reply->dp_yiaddr.s_addr == 0
|| reply->dp_yiaddr.s_addr == INADDR_BROADCAST) {
goto next_packet;
}
printf("dhcp: received ACK: server " IP_FORMAT
" IP address " IP_FORMAT "\n",
IP_LIST(&server_id), IP_LIST(&reply->dp_yiaddr));
context->iaddr = reply->dp_yiaddr;
ip = (const struct in_addr *)
dhcpol_find(&options,
dhcptag_subnet_mask_e, &len, NULL);
if (ip != NULL && len >= (int)sizeof(*ip)) {
context->netmask = *ip;
}
ip = (const struct in_addr *)
dhcpol_find(&options, dhcptag_router_e, &len, NULL);
if (ip != NULL && len >= (int)sizeof(*ip)) {
context->router = *ip;
}
dhcpol_free(&options);
untimeout((timeout_fcn_t)dhcp_timeout, &timer_arg);
break;
next_packet:
dhcpol_free(&options);
}
else if ((error != EWOULDBLOCK)) {
untimeout((timeout_fcn_t)dhcp_timeout, &timer_arg);
break;
}
else if (timer_arg == NULL) {
break;
}
else {
socket_lock(context->so, 1);
error = sbwait(&context->so->so_rcv);
socket_unlock(context->so, 1);
}
}
return (error);
}
static int
dhcp_select(struct dhcp_context * context)
{
struct timeval current_time;
int error = 0;
dhcpoa_t * options_p;
struct dhcp_packet * request;
int request_size;
int retry;
int wait_ticks;
request = dhcp_context_request(context);
options_p = &context->request_options;
make_dhcp_request(&request->dhcp, DHCP_PAYLOAD_MIN,
dhcp_msgtype_request_e,
link_address(context->dl_p), ARPHRD_ETHER,
link_address_length(context->dl_p),
options_p);
dhcpoa_add(options_p, dhcptag_requested_ip_address_e,
sizeof(context->iaddr), &context->iaddr);
dhcpoa_add(options_p, dhcptag_server_identifier_e,
sizeof(context->server_id), &context->server_id);
dhcpoa_add(options_p, dhcptag_end_e, 0, 0);
request_size = sizeof(*request) + RFC_MAGIC_SIZE
+ dhcpoa_used(options_p);
if (request_size < (int)sizeof(struct bootp_packet)) {
request_size = sizeof(struct bootp_packet);
}
init_dhcp_packet_header(request, request_size);
wait_ticks = INITIAL_WAIT_SECS * hz;
#define SELECT_RETRY_COUNT 3
for (retry = 0; retry < SELECT_RETRY_COUNT; retry++) {
printf("dhcp: sending REQUEST: server " IP_FORMAT
" IP address " IP_FORMAT "\n",
IP_LIST(&context->server_id),
IP_LIST(&context->iaddr));
microtime(¤t_time);
request->dhcp.dp_secs
= htons((u_short)
(current_time.tv_sec - context->start_time.tv_sec));
request->dhcp.dp_xid = htonl(context->xid);
#ifdef RANDOM_IP_ID
request->ip.ip_id = ip_randomid();
#else
request->ip.ip_id = htons(ip_id++);
#endif
error = send_packet(context->ifp, request, request_size);
if (error != 0) {
printf("dhcp: send_packet failed with %d\n", error);
goto failed;
}
wait_ticks += random_range(-RAND_TICKS, RAND_TICKS);
dprintf(("dhcp: waiting %d ticks\n", wait_ticks));
error = dhcp_get_ack(context, wait_ticks);
switch (error) {
case 0:
goto done;
case EPROTO:
printf("dhcp: server " IP_FORMAT " send us a NAK\n",
IP_LIST(&context->server_id));
goto failed;
case EWOULDBLOCK:
break;
default:
dprintf(("dhcp: failed to receive packets: %d\n", error));
goto failed;
}
wait_ticks *= 2;
if (wait_ticks > (MAX_WAIT_SECS * hz))
wait_ticks = MAX_WAIT_SECS * hz;
microtime(¤t_time);
}
error = ETIMEDOUT;
goto failed;
done:
error = 0;
failed:
return (error);
}
static int
dhcp_get_offer(struct dhcp_context * context, int wait_ticks)
{
int error = 0;
int gather_count = 0;
const struct in_addr * ip;
int last_rating = 0;
int len;
int n;
int rating;
struct dhcp * reply;
struct in_addr server_id;
struct socket * timer_arg;
timer_arg = context->so;
reply = dhcp_context_reply(context);
timeout((timeout_fcn_t)dhcp_timeout, &timer_arg, wait_ticks);
while (1) {
error = receive_packet(context->so, context->reply,
sizeof(context->reply), &n);
if (error == 0) {
dhcpol_t options;
dprintf(("\ndhcp: received packet length %d\n", n));
if (n < (int)sizeof(struct dhcp)) {
dprintf(("dhcp: packet is too short %d < %d\n",
n, (int)sizeof(struct dhcp)));
continue;
}
if (ntohl(reply->dp_xid) != context->xid
|| reply->dp_yiaddr.s_addr == 0
|| reply->dp_yiaddr.s_addr == INADDR_BROADCAST
|| bcmp(reply->dp_chaddr,
link_address(context->dl_p),
link_address_length(context->dl_p)) != 0) {
continue;
}
(void)dhcpol_parse_packet(&options, reply, n);
if (get_dhcp_msgtype(&options) != dhcp_msgtype_offer_e) {
goto next_packet;
}
ip = (const struct in_addr *)
dhcpol_find(&options,
dhcptag_server_identifier_e, &len, NULL);
if (ip == NULL || len < (int)sizeof(*ip)) {
goto next_packet;
}
printf("dhcp: received OFFER: server " IP_FORMAT
" IP address " IP_FORMAT "\n",
IP_LIST(ip), IP_LIST(&reply->dp_yiaddr));
server_id = *ip;
rating = rate_packet(&options);
if (rating > last_rating) {
context->iaddr = reply->dp_yiaddr;
ip = (const struct in_addr *)
dhcpol_find(&options,
dhcptag_subnet_mask_e, &len, NULL);
if (ip != NULL && len >= (int)sizeof(*ip)) {
context->netmask = *ip;
}
ip = (const struct in_addr *)
dhcpol_find(&options, dhcptag_router_e, &len, NULL);
if (ip != NULL && len >= (int)sizeof(*ip)) {
context->router = *ip;
}
context->server_id = server_id;
}
if (rating >= GOOD_RATING) {
dhcpol_free(&options);
untimeout((timeout_fcn_t)dhcp_timeout, &timer_arg);
break;
}
if (gather_count == 0) {
untimeout((timeout_fcn_t)dhcp_timeout, &timer_arg);
timer_arg = context->so;
timeout((timeout_fcn_t)dhcp_timeout, &timer_arg,
hz * GATHER_TIME_SECS);
}
gather_count = 1;
next_packet:
dhcpol_free(&options);
}
else if ((error != EWOULDBLOCK)) {
untimeout((timeout_fcn_t)dhcp_timeout, &timer_arg);
break;
}
else if (timer_arg == NULL) {
if (gather_count != 0) {
dprintf(("dhcp: gathering time has expired\n"));
error = 0;
}
break;
}
else {
socket_lock(context->so, 1);
error = sbwait(&context->so->so_rcv);
socket_unlock(context->so, 1);
}
}
return (error);
}
static int
dhcp_init(struct dhcp_context * context)
{
struct timeval current_time;
int error = 0;
uint32_t lease_option = htonl(SUGGESTED_LEASE_LENGTH);
dhcpoa_t * options_p;
struct dhcp_packet * request;
int request_size;
int retry;
int wait_ticks;
microtime(&context->start_time);
current_time = context->start_time;
request = dhcp_context_request(context);
options_p = &context->request_options;
retry:
make_dhcp_request(&request->dhcp, DHCP_PAYLOAD_MIN,
dhcp_msgtype_discover_e,
link_address(context->dl_p), ARPHRD_ETHER,
link_address_length(context->dl_p),
options_p);
dhcpoa_add(options_p, dhcptag_lease_time_e,
sizeof(lease_option), &lease_option);
dhcpoa_add(options_p, dhcptag_end_e, 0, 0);
request_size = sizeof(*request) + RFC_MAGIC_SIZE
+ dhcpoa_used(options_p);
if (request_size < (int)sizeof(struct bootp_packet)) {
request_size = sizeof(struct bootp_packet);
}
init_dhcp_packet_header(request, request_size);
wait_ticks = INITIAL_WAIT_SECS * hz;
for (retry = 0; retry < context->max_try; retry++) {
printf("dhcp: sending DISCOVER\n");
request->dhcp.dp_secs
= htons((u_short)(current_time.tv_sec
- context->start_time.tv_sec));
request->dhcp.dp_xid = htonl(context->xid);
#ifdef RANDOM_IP_ID
request->ip.ip_id = ip_randomid();
#else
request->ip.ip_id = htons(ip_id++);
#endif
error = send_packet(context->ifp, request, request_size);
if (error != 0) {
printf("dhcp: send_packet failed with %d\n", error);
goto failed;
}
wait_ticks += random_range(-RAND_TICKS, RAND_TICKS);
dprintf(("dhcp: waiting %d ticks\n", wait_ticks));
error = dhcp_get_offer(context, wait_ticks);
if (error == 0) {
error = dhcp_select(context);
if (error == 0) {
goto done;
}
if (error != EPROTO && error != ETIMEDOUT) {
dprintf(("dhcp: dhcp_select failed %d\n", error));
goto failed;
}
printf("dhcp: trying again in 10 seconds\n");
tsleep(&error, PRIBIO, "dhcp_init", 10 * hz);
context->xid++;
goto retry;
}
else if (error != EWOULDBLOCK) {
dprintf(("dhcp: failed to receive packets: %d\n", error));
goto failed;
}
wait_ticks *= 2;
if (wait_ticks > (MAX_WAIT_SECS * hz))
wait_ticks = MAX_WAIT_SECS * hz;
microtime(¤t_time);
}
error = ETIMEDOUT;
goto failed;
done:
error = 0;
failed:
return (error);
}
static void
dhcp_context_free(struct dhcp_context * context, struct proc * procp)
{
if (context == NULL) {
return;
}
if (context->so != NULL) {
int error;
context->ifr.ifr_intval = 0;
error = ifioctl(context->so, SIOCAUTOADDR,
(caddr_t)&context->ifr, procp);
if (error) {
printf("dhcp: SIOCAUTOADDR failed: %d\n", error);
}
soclose(context->so);
}
kfree(context, sizeof(*context));
return;
}
static struct dhcp_context *
dhcp_context_create(struct ifnet * ifp, int max_try,
struct proc * procp, int * error_p)
{
struct dhcp_context * context = NULL;
struct sockaddr_dl * dl_p;
struct in_addr lo_addr;
struct in_addr lo_mask;
int error;
struct sockaddr_in sin;
dl_p = link_from_ifnet(ifp);
if (dl_p == NULL) {
printf("dhcp: can't get link address\n");
error = ENXIO;
goto failed;
}
printf("dhcp: h/w addr ");
link_print(dl_p);
if (dl_p->sdl_type != IFT_ETHER) {
printf("dhcp: hardware type %d not supported\n",
dl_p->sdl_type);
error = ENXIO;
goto failed;
}
context = (struct dhcp_context *)kalloc(sizeof(*context));
if (context == NULL) {
printf("dhcp: failed to allocate context\n");
error = ENOMEM;
goto failed;
}
bzero(context, sizeof(*context));
error = socreate(AF_INET, &context->so, SOCK_DGRAM, 0);
if (error != 0) {
printf("dhcp: socreate failed %d\n", error);
goto failed;
}
lo_addr.s_addr = htonl(INADDR_LOOPBACK);
lo_mask.s_addr = htonl(IN_CLASSA_NET);
error = inet_aifaddr(context->so, "lo0", &lo_addr, &lo_mask, NULL);
if (error != 0) {
printf("dhcp: assigning loopback address failed %d\n", error);
}
snprintf(context->ifr.ifr_name,
sizeof(context->ifr.ifr_name), "%s%d", ifp->if_name,
ifp->if_unit);
context->ifr.ifr_intval = 1;
error = ifioctl(context->so, SIOCAUTOADDR, (caddr_t)&context->ifr, procp);
if (error) {
printf("dhcp: SIOCAUTOADDR failed: %d\n", error);
goto failed;
}
dprintf(("dhcp: SIOCAUTOADDR done\n"));
error = ifioctl(context->so, SIOCPROTOATTACH, (caddr_t)&context->ifr,
procp);
if (error) {
printf("dhcp: SIOCPROTOATTACH failed: %d\n", error);
goto failed;
}
dprintf(("dhcp: SIOCPROTOATTACH done\n"));
sin.sin_len = sizeof(sin);
sin.sin_family = AF_INET;
sin.sin_port = htons(IPPORT_BOOTPC);
sin.sin_addr.s_addr = INADDR_ANY;
error = sobind(context->so, (struct sockaddr *)&sin);
if (error) {
printf("dhcp: sobind failed, %d\n", error);
goto failed;
}
socket_lock(context->so, 1);
context->so->so_state |= SS_NBIO;
socket_unlock(context->so, 1);
context->max_try = max_try;
context->dl_p = dl_p;
context->ifp = ifp;
context->xid = random();
return (context);
failed:
dhcp_context_free(context, procp);
*error_p = error;
return (NULL);
}
int
dhcp(struct ifnet * ifp, struct in_addr * iaddr_p, int max_try,
struct in_addr * netmask_p, struct in_addr * router_p,
struct proc * procp)
{
int error = 0;
struct dhcp_context * context;
context = dhcp_context_create(ifp, max_try, procp, &error);
if (context == NULL) {
return (error);
}
error = dhcp_init(context);
if (error == 0) {
*iaddr_p = context->iaddr;
*netmask_p = context->netmask;
*router_p = context->router;
}
dhcp_context_free(context, procp);
return (error);
}