int main() { int i; dhcpol_t options; char error[256]; struct dhcp * pkt = (struct dhcp *)buf; dhcpol_init(&options); for (i = 0; tests[i].name; i++) { printf("\nTest %d: ", i); bcopy(tests[i].data, pkt->dp_options, tests[i].len); if (dhcpol_parse_packet(&options, pkt, sizeof(*pkt) + tests[i].len, error) != tests[i].result) { printf("test '%s' FAILED\n", tests[i].name); if (tests[i].result == TRUE) { printf("error message returned was %s\n", error); } } else { printf("test '%s' PASSED\n", tests[i].name); if (tests[i].result == FALSE) { printf("error message returned was %s\n", error); } } dhcpol_free(&options); } exit(0); }
static boolean_t get_ip_parameters(struct in_addr * iaddr_p, struct in_addr * netmask_p, struct in_addr * router_p) { void * entry; const void * pkt; int pkt_len; entry = IOBSDRegistryEntryForDeviceTree("/chosen"); if (entry == NULL) { return (FALSE); } pkt = IOBSDRegistryEntryGetData(entry, DHCP_RESPONSE, &pkt_len); if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) { printf("netboot: retrieving IP information from DHCP response\n"); } else { pkt = IOBSDRegistryEntryGetData(entry, BOOTP_RESPONSE, &pkt_len); if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) { printf("netboot: retrieving IP information from BOOTP response\n"); } } if (pkt != NULL) { const struct in_addr * ip; int len; dhcpol_t options; const struct dhcp * reply; reply = (const struct dhcp *)pkt; (void)dhcpol_parse_packet(&options, reply, pkt_len); *iaddr_p = reply->dp_yiaddr; ip = (const struct in_addr *) dhcpol_find(&options, dhcptag_subnet_mask_e, &len, NULL); if (ip) { *netmask_p = *ip; } ip = (const struct in_addr *) dhcpol_find(&options, dhcptag_router_e, &len, NULL); if (ip) { *router_p = *ip; } } IOBSDRegistryEntryRelease(entry); return (pkt != NULL); }
PRIVATE_EXTERN void bsdp_print_packet(struct dhcp * pkt, int length, int options_only) { dhcpo_err_str_t err; int i; dhcpol_t options; dhcpol_t vendor_options; dhcpol_init(&options); dhcpol_init(&vendor_options); if (options_only == 0) { dhcp_packet_print(pkt, length); } if (dhcpol_parse_packet(&options, pkt, length, &err) == FALSE) { fprintf(stderr, "packet did not parse, %s\n", err.str); return; } if (dhcpol_parse_vendor(&vendor_options, &options, &err) == FALSE) { fprintf(stderr, "vendor options did not parse, %s\n", err.str); goto done; } printf("BSDP Options count is %d\n", dhcpol_count(&vendor_options)); for (i = 0; i < dhcpol_count(&vendor_options); i++) { u_int8_t code; u_int8_t * opt = dhcpol_element(&vendor_options, i); u_int8_t len; code = opt[TAG_OFFSET]; len = opt[LEN_OFFSET]; printf("%s: ", bsdptag_name(code)); if (code == bsdptag_message_type_e) { printf("%s (", bsdp_msgtype_names(opt[OPTION_OFFSET])); dhcptype_print(bsdptag_type(code), opt + OPTION_OFFSET, len); printf(")\n"); } else { dhcptype_print(bsdptag_type(code), opt + OPTION_OFFSET, len); printf("\n"); } } done: dhcpol_free(&options); dhcpol_free(&vendor_options); return; }
static boolean_t get_root_path(char * root_path) { void * entry; boolean_t found = FALSE; const void * pkt; int pkt_len; entry = IOBSDRegistryEntryForDeviceTree("/chosen"); if (entry == NULL) { return (FALSE); } pkt = IOBSDRegistryEntryGetData(entry, BSDP_RESPONSE, &pkt_len); if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) { printf("netboot: retrieving root path from BSDP response\n"); } else { pkt = IOBSDRegistryEntryGetData(entry, BOOTP_RESPONSE, &pkt_len); if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) { printf("netboot: retrieving root path from BOOTP response\n"); } } if (pkt != NULL) { int len; dhcpol_t options; const char * path; const struct dhcp * reply; reply = (const struct dhcp *)pkt; (void)dhcpol_parse_packet(&options, reply, pkt_len); path = (const char *)dhcpol_find(&options, dhcptag_root_path_e, &len, NULL); if (path) { memcpy(root_path, path, len); root_path[len] = '\0'; found = TRUE; } } IOBSDRegistryEntryRelease(entry); return (found); }
static int S_bsdp_option(mach_port_t server, int argc, char * argv[]) { CFDictionaryRef chosen = NULL; void * data = NULL; int data_len; struct dhcp * dhcp; int length; dhcpol_t options; CFDataRef response = NULL; int ret = 1; int tag = 0; dhcpol_t vendor_options; int vendor_tag = 0; if (getuid() != 0) { return (EX_NOPERM); } chosen = myIORegistryEntryCopyValue("IODeviceTree:/chosen"); if (chosen == NULL) { goto done; } response = CFDictionaryGetValue(chosen, CFSTR("bsdp-response")); if (isA_CFData(response) == NULL) { response = CFDictionaryGetValue(chosen, CFSTR("bootp-response")); if (isA_CFData(response) == NULL) { goto done; } } /* ALIGN: CFDataGetBytePtr is aligned to at least sizeof(uint64) */ dhcp = (struct dhcp *)(void *)CFDataGetBytePtr(response); length = (int)CFDataGetLength(response); if (dhcpol_parse_packet(&options, dhcp, length, NULL) == FALSE) { goto done; } if (strcmp(argv[0], SHADOW_MOUNT_PATH_COMMAND) == 0) { tag = dhcptag_vendor_specific_e; vendor_tag = bsdptag_shadow_mount_path_e; } else if (strcmp(argv[0], SHADOW_FILE_PATH_COMMAND) == 0) { tag = dhcptag_vendor_specific_e; vendor_tag = bsdptag_shadow_file_path_e; } else if (strcmp(argv[0], MACHINE_NAME_COMMAND) == 0) { tag = dhcptag_vendor_specific_e; vendor_tag = bsdptag_machine_name_e; } else { tag = atoi(argv[0]); if (argc == 2) { vendor_tag = atoi(argv[1]); } } if (tag == dhcptag_vendor_specific_e && vendor_tag != 0) { if (dhcpol_parse_vendor(&vendor_options, &options, NULL) == FALSE) { goto done; } data = dhcpol_option_copy(&vendor_options, vendor_tag, &data_len); if (data != NULL) { dhcptype_print(bsdptag_type(vendor_tag), data, data_len); ret = 0; } dhcpol_free(&vendor_options); } else { const dhcptag_info_t * entry; entry = dhcptag_info(tag); if (entry == NULL) { goto done; } data = dhcpol_option_copy(&options, tag, &data_len); if (data != NULL) { dhcptype_print(entry->type, data, data_len); ret = 0; } } done: if (data != NULL) { free(data); } if (chosen != NULL) { CFRelease(chosen); } return (ret); }
/* * Function: DHCPLeaseCreateWithDictionary * Purpose: * Instantiate a new DHCPLease structure corresponding to the given * dictionary. Validates that required properties are present, * returns NULL if those checks fail. */ static DHCPLeaseRef DHCPLeaseCreateWithDictionary(CFDictionaryRef dict, bool is_wifi) { CFDataRef hwaddr_data; dhcp_lease_time_t lease_time; DHCPLeaseRef lease_p; CFDataRef pkt_data; CFRange pkt_data_range; struct in_addr * router_p; CFStringRef ssid = NULL; CFDateRef start_date; dhcp_lease_time_t t1_time; dhcp_lease_time_t t2_time; /* get the lease start time */ start_date = CFDictionaryGetValue(dict, kLeaseStartDate); if (isA_CFDate(start_date) == NULL) { goto failed; } /* get the packet data */ pkt_data = CFDictionaryGetValue(dict, kPacketData); if (isA_CFData(pkt_data) == NULL) { goto failed; } /* if Wi-Fi, get the SSID */ if (is_wifi) { ssid = CFDictionaryGetValue(dict, kSSID); if (isA_CFString(ssid) == NULL) { goto failed; } } pkt_data_range.location = 0; pkt_data_range.length = CFDataGetLength(pkt_data); if (pkt_data_range.length < sizeof(struct dhcp)) { goto failed; } lease_p = (DHCPLeaseRef) malloc(offsetof(DHCPLease, pkt) + pkt_data_range.length); bzero(lease_p, offsetof(DHCPLease, pkt)); /* copy the packet data */ CFDataGetBytes(pkt_data, pkt_data_range, lease_p->pkt); lease_p->pkt_length = (int)pkt_data_range.length; /* get the lease information and router IP address */ lease_p->lease_start = (absolute_time_t)CFDateGetAbsoluteTime(start_date); { /* parse/retrieve options */ dhcpol_t options; (void)dhcpol_parse_packet(&options, (void *)lease_p->pkt, (int)pkt_data_range.length, NULL); dhcp_get_lease_from_options(&options, &lease_time, &t1_time, &t2_time); router_p = dhcp_get_router_from_options(&options, lease_p->our_ip); dhcpol_free(&options); } lease_p->lease_length = lease_time; /* get the IP address */ /* ALIGN: lease_p->pkt is aligned, cast ok. */ lease_p->our_ip = ((struct dhcp *)(void *)lease_p->pkt)->dp_yiaddr; /* get the router information */ if (router_p != NULL) { CFRange hwaddr_range; lease_p->router_ip = *router_p; /* get the router hardware address */ hwaddr_data = CFDictionaryGetValue(dict, kRouterHardwareAddress); hwaddr_range.length = 0; if (isA_CFData(hwaddr_data) != NULL) { hwaddr_range.length = CFDataGetLength(hwaddr_data); } if (hwaddr_range.length > 0) { hwaddr_range.location = 0; if (hwaddr_range.length > sizeof(lease_p->router_hwaddr)) { hwaddr_range.length = sizeof(lease_p->router_hwaddr); } lease_p->router_hwaddr_length = (int)hwaddr_range.length; CFDataGetBytes(hwaddr_data, hwaddr_range, lease_p->router_hwaddr); } } if (ssid != NULL) { CFRetain(ssid); lease_p->ssid = ssid; } return (lease_p); failed: return (NULL); }
void dhcp_packet_print_cfstr(CFMutableStringRef str, struct dhcp * dp, int pkt_len) { int i; int j; int len; if (pkt_len < sizeof(struct dhcp)) { STRING_APPEND(str, "Packet is too short %d < %d\n", pkt_len, (int)sizeof(struct dhcp)); return; } STRING_APPEND(str, "op = "); if (dp->dp_op == BOOTREQUEST) { STRING_APPEND(str, "BOOTREQUEST\n"); } else if (dp->dp_op == BOOTREPLY) { STRING_APPEND(str, "BOOTREPLY\n"); } else { i = dp->dp_op; STRING_APPEND(str, "OP(%d)\n", i); } i = dp->dp_htype; STRING_APPEND(str, "htype = %d\n", i); STRING_APPEND(str, "flags = %x\n", ntohs(dp->dp_flags)); len = dp->dp_hlen; STRING_APPEND(str, "hlen = %d\n", len); i = dp->dp_hops; STRING_APPEND(str, "hops = %d\n", i); STRING_APPEND(str, "xid = %lu\n", (u_long)ntohl(dp->dp_xid)); STRING_APPEND(str, "secs = %hu\n", ntohs(dp->dp_secs)); STRING_APPEND(str, "ciaddr = %s\n", inet_ntoa(dp->dp_ciaddr)); STRING_APPEND(str, "yiaddr = %s\n", inet_ntoa(dp->dp_yiaddr)); STRING_APPEND(str, "siaddr = %s\n", inet_ntoa(dp->dp_siaddr)); STRING_APPEND(str, "giaddr = %s\n", inet_ntoa(dp->dp_giaddr)); STRING_APPEND(str, "chaddr = "); for (j = 0; j < len; j++) { i = dp->dp_chaddr[j]; STRING_APPEND(str, "%0x", i); if (j < (len - 1)) STRING_APPEND(str, ":"); } STRING_APPEND(str, "\n"); STRING_APPEND(str, "sname = %s\n", dp->dp_sname); STRING_APPEND(str, "file = %s\n", dp->dp_file); { dhcpol_t t; dhcpol_init(&t); if (dhcpol_parse_packet(&t, dp, pkt_len, NULL)) { STRING_APPEND(str, "options:\n"); dhcpol_print_cfstr(str, &t); } dhcpol_free(&t); } return; }
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) { /* not for us */ continue; } (void)dhcpol_parse_packet(&options, reply, n); if (get_dhcp_msgtype(&options) != dhcp_msgtype_offer_e) { /* not an offer */ goto next_packet; } ip = (const struct in_addr *) dhcpol_find(&options, dhcptag_server_identifier_e, &len, NULL); if (ip == NULL || len < (int)sizeof(*ip)) { /* missing/invalid server identifier */ 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); /* packet is good enough */ 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) { /* timed out */ 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_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) { /* not for us */ 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) { /* server NAK'd us, start over */ 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) { /* ignore the packet */ 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)) { /* if some other error occurred, we're done */ untimeout((timeout_fcn_t)dhcp_timeout, &timer_arg); break; } else if (timer_arg == NULL) { /* timed out */ break; } else { /* wait for a wait to arrive, or a timeout to occur */ socket_lock(context->so, 1); error = sbwait(&context->so->so_rcv); socket_unlock(context->so, 1); } } return (error); }