/* * get the vps and put them in perl hash * If one VP have multiple values it is added as array_ref * Example for this is Cisco-AVPair that holds multiple values. * Which will be available as array_ref in $RAD_REQUEST{'Cisco-AVPair'} */ static void perl_store_vps(UNUSED TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **vps, HV *rad_hv, const char *hash_name, const char *list_name) { VALUE_PAIR *vp; hv_undef(rad_hv); fr_cursor_t cursor; RINDENT(); fr_pair_list_sort(vps, fr_pair_cmp_by_da_tag); for (vp = fr_cursor_init(&cursor, vps); vp; vp = fr_cursor_next(&cursor)) { VALUE_PAIR *next; char const *name; char namebuf[256]; /* * Tagged attributes are added to the hash with name * <attribute>:<tag>, others just use the normal attribute * name as the key. */ if (vp->da->flags.has_tag && (vp->tag != TAG_ANY)) { snprintf(namebuf, sizeof(namebuf), "%s:%d", vp->da->name, vp->tag); name = namebuf; } else { name = vp->da->name; } /* * We've sorted by type, then tag, so attributes of the * same type/tag should follow on from each other. */ if ((next = fr_cursor_next_peek(&cursor)) && ATTRIBUTE_EQ(vp, next)) { int i = 0; AV *av; av = newAV(); perl_vp_to_svpvn_element(request, av, vp, &i, hash_name, list_name); do { perl_vp_to_svpvn_element(request, av, next, &i, hash_name, list_name); fr_cursor_next(&cursor); } while ((next = fr_cursor_next_peek(&cursor)) && ATTRIBUTE_EQ(vp, next)); (void)hv_store(rad_hv, name, strlen(name), newRV_noinc((SV *)av), 0); continue; } /* * It's a normal single valued attribute */ switch (vp->vp_type) { case FR_TYPE_STRING: RDEBUG2("$%s{'%s'} = &%s:%s -> '%pV'", hash_name, vp->da->name, list_name, vp->da->name, &vp->data); (void)hv_store(rad_hv, name, strlen(name), newSVpvn(vp->vp_strvalue, vp->vp_length), 0); break; case FR_TYPE_OCTETS: RDEBUG2("$%s{'%s'} = &%s:%s -> %pV", hash_name, vp->da->name, list_name, vp->da->name, &vp->data); (void)hv_store(rad_hv, name, strlen(name), newSVpvn((char const *)vp->vp_octets, vp->vp_length), 0); break; default: { char buffer[1024]; size_t len; len = fr_pair_value_snprint(buffer, sizeof(buffer), vp, '\0'); RDEBUG2("$%s{'%s'} = &%s:%s -> '%s'", hash_name, vp->da->name, list_name, vp->da->name, buffer); (void)hv_store(rad_hv, name, strlen(name), newSVpvn(buffer, truncate_len(len, sizeof(buffer))), 0); } break; } } REXDENT(); }
/* * Receive one packet, maybe. */ static int recv_one_packet(int wait_time) { fd_set set; struct timeval tv; rc_request_t *request; RADIUS_PACKET *reply, **packet_p; volatile int max_fd; /* And wait for reply, timing out as necessary */ FD_ZERO(&set); max_fd = fr_packet_list_fd_set(pl, &set); if (max_fd < 0) exit(1); /* no sockets to listen on! */ tv.tv_sec = (wait_time <= 0) ? 0 : wait_time; tv.tv_usec = 0; /* * No packet was received. */ if (select(max_fd, &set, NULL, NULL, &tv) <= 0) return 0; /* * Look for the packet. */ reply = fr_packet_list_recv(pl, &set); if (!reply) { ERROR("Received bad packet"); #ifdef WITH_TCP /* * If the packet is bad, we close the socket. * I'm not sure how to do that now, so we just * die... */ if (proto) exit(1); #endif return -1; /* bad packet */ } /* * We don't use udpfromto. So if we bind to "*", we want * to find replies sent to 192.0.2.4. Therefore, we * force all replies to have the one address we know * about, no matter what real address they were sent to. * * This only works if were not using any of the * Packet-* attributes, or running with 'auto'. */ reply->dst_ipaddr = client_ipaddr; reply->dst_port = client_port; #ifdef WITH_TCP /* * TCP sockets don't use recvmsg(), and thus don't get * the source IP/port. However, since they're TCP, we * know what the source IP/port is, because that's where * we connected to. */ if (ipproto == IPPROTO_TCP) { reply->src_ipaddr = server_ipaddr; reply->src_port = server_port; } #endif packet_p = fr_packet_list_find_byreply(pl, reply); if (!packet_p) { ERROR("Received reply to request we did not send. (id=%d socket %d)", reply->id, reply->sockfd); rad_free(&reply); return -1; /* got reply to packet we didn't send */ } request = fr_packet2myptr(rc_request_t, packet, packet_p); /* * Fails the signature validation: not a real reply. * FIXME: Silently drop it and listen for another packet. */ if (rad_verify(reply, request->packet, secret) < 0) { REDEBUG("Reply verification failed"); stats.lost++; goto packet_done; /* shared secret is incorrect */ } if (print_filename) { RDEBUG("%s response code %d", request->files->packets, reply->code); } deallocate_id(request); request->reply = reply; reply = NULL; /* * If this fails, we're out of memory. */ if (rad_decode(request->reply, request->packet, secret) != 0) { REDEBUG("Reply decode failed"); stats.lost++; goto packet_done; } fr_packet_header_print(fr_log_fp, request->reply, true); if (fr_debug_lvl > 0) vp_printlist(fr_log_fp, request->reply->vps); /* * Increment counters... */ switch (request->reply->code) { case PW_CODE_ACCESS_ACCEPT: case PW_CODE_ACCOUNTING_RESPONSE: case PW_CODE_COA_ACK: case PW_CODE_DISCONNECT_ACK: stats.accepted++; break; case PW_CODE_ACCESS_CHALLENGE: break; default: stats.rejected++; } /* * If we had an expected response code, check to see if the * packet matched that. */ if (request->reply->code != request->filter_code) { if (is_radius_code(request->reply->code)) { REDEBUG("%s: Expected %s got %s", request->name, fr_packet_codes[request->filter_code], fr_packet_codes[request->reply->code]); } else { REDEBUG("%s: Expected %u got %i", request->name, request->filter_code, request->reply->code); } stats.failed++; /* * Check if the contents of the packet matched the filter */ } else if (!request->filter) { stats.passed++; } else { VALUE_PAIR const *failed[2]; fr_pair_list_sort(&request->reply->vps, fr_pair_cmp_by_da_tag); if (fr_pair_validate(failed, request->filter, request->reply->vps)) { RDEBUG("%s: Response passed filter", request->name); stats.passed++; } else { fr_pair_validate_debug(request, failed); REDEBUG("%s: Response for failed filter", request->name); stats.failed++; } } if (request->resend == resend_count) { request->done = true; } packet_done: rad_free(&request->reply); rad_free(&reply); /* may be NULL */ return 0; }
/* * get the vps and put them in perl hash * If one VP have multiple values it is added as array_ref * Example for this is Cisco-AVPair that holds multiple values. * Which will be available as array_ref in $RAD_REQUEST{'Cisco-AVPair'} */ static void perl_store_vps(UNUSED TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **vps, HV *rad_hv, const char *hash_name, const char *list_name) { VALUE_PAIR *vp; char *tbuff; size_t tbufflen = 1024; hv_undef(rad_hv); vp_cursor_t cursor; /* * Find out how much room to allocate. */ for (vp = fr_cursor_init(&cursor, vps); vp; vp = fr_cursor_next(&cursor)) { if (((vp->length * 2) + 3) > tbufflen) { tbufflen = (vp->vp_length * 2) + 3; } } tbuff = talloc_array(request, char, tbufflen); RINDENT(); fr_pair_list_sort(vps, fr_pair_cmp_by_da_tag); for (vp = fr_cursor_init(&cursor, vps); vp; vp = fr_cursor_next(&cursor)) { VALUE_PAIR *next; char const *name; size_t len; char namebuf[256]; /* * Tagged attributes are added to the hash with name * <attribute>:<tag>, others just use the normal attribute * name as the key. */ if (vp->da->flags.has_tag && (vp->tag != TAG_ANY)) { snprintf(namebuf, sizeof(namebuf), "%s:%d", vp->da->name, vp->tag); name = namebuf; } else { name = vp->da->name; } /* * We've sorted by type, then tag, so attributes of the * same type/tag should follow on from each other. */ if ((next = fr_cursor_next_peek(&cursor)) && ATTRIBUTE_EQ(vp, next)) { int i = 0; AV *av; av = newAV(); perl_vp_to_svpvn_element(request, av, vp, &i, hash_name, list_name); do { perl_vp_to_svpvn_element(request, av, next, &i, hash_name, list_name); fr_cursor_next(&cursor); } while ((next = fr_cursor_next_peek(&cursor)) && ATTRIBUTE_EQ(vp, next)); (void)hv_store(rad_hv, name, strlen(name), newRV_noinc((SV *)av), 0); continue; } /* * It's a normal single valued attribute */ switch (vp->da->type) { case PW_TYPE_STRING: RDEBUG("$%s{'%s'} = &%s:%s -> '%s'", hash_name, vp->da->name, list_name, vp->da->name, vp->vp_strvalue); (void)hv_store(rad_hv, name, strlen(name), newSVpvn(vp->vp_strvalue, vp->vp_length), 0); break; default: len = vp_prints_value(tbuff, tbufflen, vp, 0); RDEBUG("$%s{'%s'} = &%s:%s -> '%s'", hash_name, vp->da->name, list_name, vp->da->name, tbuff); (void)hv_store(rad_hv, name, strlen(name), newSVpvn(tbuff, truncate_len(len, tbufflen)), 0); break; } } REXDENT(); talloc_free(tbuff); }
/* * Initialize a radclient data structure and add it to * the global linked list. */ static int radclient_init(TALLOC_CTX *ctx, rc_file_pair_t *files) { FILE *packets, *filters = NULL; vp_cursor_t cursor; VALUE_PAIR *vp; rc_request_t *request; bool packets_done = false; uint64_t num = 0; assert(files->packets != NULL); /* * Determine where to read the VP's from. */ if (strcmp(files->packets, "-") != 0) { packets = fopen(files->packets, "r"); if (!packets) { ERROR("Error opening %s: %s", files->packets, strerror(errno)); return 0; } /* * Read in the pairs representing the expected response. */ if (files->filters) { filters = fopen(files->filters, "r"); if (!filters) { ERROR("Error opening %s: %s", files->filters, strerror(errno)); fclose(packets); return 0; } } } else { packets = stdin; } /* * Loop until the file is done. */ do { /* * Allocate it. */ request = talloc_zero(ctx, rc_request_t); if (!request) { ERROR("Out of memory"); goto error; } request->packet = rad_alloc(request, true); if (!request->packet) { ERROR("Out of memory"); goto error; } #ifdef WITH_TCP request->packet->src_ipaddr = client_ipaddr; request->packet->src_port = client_port; request->packet->dst_ipaddr = server_ipaddr; request->packet->dst_port = server_port; request->packet->proto = ipproto; #endif request->files = files; request->packet->id = -1; /* allocate when sending */ request->num = num++; /* * Read the request VP's. */ if (fr_pair_list_afrom_file(request->packet, &request->packet->vps, packets, &packets_done) < 0) { char const *input; if ((files->packets[0] == '-') && (files->packets[1] == '\0')) { input = "stdin"; } else { input = files->packets; } REDEBUG("Error parsing \"%s\"", input); goto error; } /* * Skip empty entries */ if (!request->packet->vps) { talloc_free(request); continue; } /* * Read in filter VP's. */ if (filters) { bool filters_done; if (fr_pair_list_afrom_file(request, &request->filter, filters, &filters_done) < 0) { REDEBUG("Error parsing \"%s\"", files->filters); goto error; } if (filters_done && !packets_done) { REDEBUG("Differing number of packets/filters in %s:%s " "(too many requests))", files->packets, files->filters); goto error; } if (!filters_done && packets_done) { REDEBUG("Differing number of packets/filters in %s:%s " "(too many filters))", files->packets, files->filters); goto error; } /* * xlat expansions aren't supported here */ for (vp = fr_cursor_init(&cursor, &request->filter); vp; vp = fr_cursor_next(&cursor)) { if (vp->type == VT_XLAT) { vp->type = VT_DATA; vp->vp_strvalue = vp->xlat; vp->vp_length = talloc_array_length(vp->vp_strvalue) - 1; } if (vp->da->vendor == 0 ) switch (vp->da->attr) { case PW_RESPONSE_PACKET_TYPE: case PW_PACKET_TYPE: fr_cursor_remove(&cursor); /* so we don't break the filter */ request->filter_code = vp->vp_integer; talloc_free(vp); default: break; } } /* * This allows efficient list comparisons later */ fr_pair_list_sort(&request->filter, fr_pair_cmp_by_da_tag); } /* * Process special attributes */ for (vp = fr_cursor_init(&cursor, &request->packet->vps); vp; vp = fr_cursor_next(&cursor)) { /* * Double quoted strings get marked up as xlat expansions, * but we don't support that in request. */ if (vp->type == VT_XLAT) { vp->type = VT_DATA; vp->vp_strvalue = vp->xlat; vp->vp_length = talloc_array_length(vp->vp_strvalue) - 1; } if (!vp->da->vendor) switch (vp->da->attr) { default: break; /* * Allow it to set the packet type in * the attributes read from the file. */ case PW_PACKET_TYPE: request->packet->code = vp->vp_integer; break; case PW_RESPONSE_PACKET_TYPE: request->filter_code = vp->vp_integer; break; case PW_PACKET_DST_PORT: request->packet->dst_port = (vp->vp_integer & 0xffff); break; case PW_PACKET_DST_IP_ADDRESS: request->packet->dst_ipaddr.af = AF_INET; request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; request->packet->dst_ipaddr.prefix = 32; break; case PW_PACKET_DST_IPV6_ADDRESS: request->packet->dst_ipaddr.af = AF_INET6; request->packet->dst_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr; request->packet->dst_ipaddr.prefix = 128; break; case PW_PACKET_SRC_PORT: if ((vp->vp_integer < 1024) || (vp->vp_integer > 65535)) { ERROR("Invalid value '%u' for Packet-Src-Port", vp->vp_integer); goto error; } request->packet->src_port = (vp->vp_integer & 0xffff); break; case PW_PACKET_SRC_IP_ADDRESS: request->packet->src_ipaddr.af = AF_INET; request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; request->packet->src_ipaddr.prefix = 32; break; case PW_PACKET_SRC_IPV6_ADDRESS: request->packet->src_ipaddr.af = AF_INET6; request->packet->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr; request->packet->src_ipaddr.prefix = 128; break; case PW_DIGEST_REALM: case PW_DIGEST_NONCE: case PW_DIGEST_METHOD: case PW_DIGEST_URI: case PW_DIGEST_QOP: case PW_DIGEST_ALGORITHM: case PW_DIGEST_BODY_DIGEST: case PW_DIGEST_CNONCE: case PW_DIGEST_NONCE_COUNT: case PW_DIGEST_USER_NAME: /* overlapping! */ { DICT_ATTR const *da; uint8_t *p, *q; p = talloc_array(vp, uint8_t, vp->vp_length + 2); memcpy(p + 2, vp->vp_octets, vp->vp_length); p[0] = vp->da->attr - PW_DIGEST_REALM + 1; vp->vp_length += 2; p[1] = vp->vp_length; da = dict_attrbyvalue(PW_DIGEST_ATTRIBUTES, 0); if (!da) { ERROR("Out of memory"); goto error; } vp->da = da; /* * Re-do fr_pair_value_memsteal ourselves, * because we play games with * vp->da, and fr_pair_value_memsteal goes * to GREAT lengths to sanitize * and fix and change and * double-check the various * fields. */ memcpy(&q, &vp->vp_octets, sizeof(q)); talloc_free(q); vp->vp_octets = talloc_steal(vp, p); vp->type = VT_DATA; VERIFY_VP(vp); } break; /* * Cache this for later. */ case PW_CLEARTEXT_PASSWORD: request->password = vp; break; /* * Keep a copy of the the password attribute. */ case PW_CHAP_PASSWORD: /* * If it's already hex, do nothing. */ if ((vp->vp_length == 17) && (already_hex(vp))) break; /* * CHAP-Password is octets, so it may not be zero terminated. */ request->password = fr_pair_make(request->packet, &request->packet->vps, "Cleartext-Password", "", T_OP_EQ); fr_pair_value_bstrncpy(request->password, vp->vp_strvalue, vp->vp_length); break; case PW_USER_PASSWORD: case PW_MS_CHAP_PASSWORD: request->password = fr_pair_make(request->packet, &request->packet->vps, "Cleartext-Password", vp->vp_strvalue, T_OP_EQ); break; case PW_RADCLIENT_TEST_NAME: request->name = vp->vp_strvalue; break; } } /* loop over the VP's we read in */ /* * Use the default set on the command line */ if (request->packet->code == PW_CODE_UNDEFINED) request->packet->code = packet_code; /* * Default to the filename */ if (!request->name) request->name = request->files->packets; /* * Automatically set the response code from the request code * (if one wasn't already set). */ if (request->filter_code == PW_CODE_UNDEFINED) { switch (request->packet->code) { case PW_CODE_ACCESS_REQUEST: request->filter_code = PW_CODE_ACCESS_ACCEPT; break; case PW_CODE_ACCOUNTING_REQUEST: request->filter_code = PW_CODE_ACCOUNTING_RESPONSE; break; case PW_CODE_COA_REQUEST: request->filter_code = PW_CODE_COA_ACK; break; case PW_CODE_DISCONNECT_REQUEST: request->filter_code = PW_CODE_DISCONNECT_ACK; break; case PW_CODE_STATUS_SERVER: switch (radclient_get_code(request->packet->dst_port)) { case PW_CODE_ACCESS_REQUEST: request->filter_code = PW_CODE_ACCESS_ACCEPT; break; case PW_CODE_ACCOUNTING_REQUEST: request->filter_code = PW_CODE_ACCOUNTING_RESPONSE; break; default: REDEBUG("Can't determine expected response to Status-Server request, specify " "a well known RADIUS port, or add a Response-Packet-Type attribute " "to the request of filter"); goto error; } break; case PW_CODE_UNDEFINED: REDEBUG("Both Packet-Type and Response-Packet-Type undefined, specify at least one, " "or a well known RADIUS port"); goto error; default: REDEBUG("Can't determine expected Response-Packet-Type for Packet-Type %i", request->packet->code); goto error; } /* * Automatically set the request code from the response code * (if one wasn't already set). */ } else if (request->packet->code == PW_CODE_UNDEFINED) { switch (request->filter_code) { case PW_CODE_ACCESS_ACCEPT: case PW_CODE_ACCESS_REJECT: request->packet->code = PW_CODE_ACCESS_REQUEST; break; case PW_CODE_ACCOUNTING_RESPONSE: request->packet->code = PW_CODE_ACCOUNTING_REQUEST; break; case PW_CODE_DISCONNECT_ACK: case PW_CODE_DISCONNECT_NAK: request->packet->code = PW_CODE_DISCONNECT_REQUEST; break; case PW_CODE_COA_ACK: case PW_CODE_COA_NAK: request->packet->code = PW_CODE_COA_REQUEST; break; default: REDEBUG("Can't determine expected Packet-Type for Response-Packet-Type %i", request->filter_code); goto error; } } /* * Automatically set the dst port (if one wasn't already set). */ if (request->packet->dst_port == 0) { radclient_get_port(request->packet->code, &request->packet->dst_port); if (request->packet->dst_port == 0) { REDEBUG("Can't determine destination port"); goto error; } } /* * Add it to the tail of the list. */ if (!request_head) { assert(rc_request_tail == NULL); request_head = request; request->prev = NULL; } else { assert(rc_request_tail->next == NULL); rc_request_tail->next = request; request->prev = rc_request_tail; } rc_request_tail = request; request->next = NULL; /* * Set the destructor so it removes itself from the * request list when freed. We don't set this until * the packet is actually in the list, else we trigger * the asserts in the free callback. */ talloc_set_destructor(request, _rc_request_free); } while (!packets_done); /* loop until the file is done. */ if (packets != stdin) fclose(packets); if (filters) fclose(filters); /* * And we're done. */ return 1; error: talloc_free(request); if (packets != stdin) fclose(packets); if (filters) fclose(filters); return 0; }
ssize_t fr_dhcpv4_encode(uint8_t *buffer, size_t buflen, dhcp_packet_t *original, int code, uint32_t xid, VALUE_PAIR *vps) { uint8_t *p; fr_cursor_t cursor; VALUE_PAIR *vp; uint32_t lvalue; uint16_t svalue; size_t dhcp_size; ssize_t len; p = buffer; if (buflen < DEFAULT_PACKET_SIZE) return -1; /* * @todo: Make this work again. */ #if 0 mms = DEFAULT_PACKET_SIZE; /* maximum message size */ /* * Clients can request a LARGER size, but not a * smaller one. They also cannot request a size * larger than MTU. */ /* DHCP-DHCP-Maximum-Msg-Size */ vp = fr_pair_find_by_da(vps, attr_dhcp_dhcp_maximum_msg_size, TAG_ANY); if (vp && (vp->vp_uint32 > mms)) { mms = vp->vp_uint32; if (mms > MAX_PACKET_SIZE) mms = MAX_PACKET_SIZE; } #endif vp = fr_pair_find_by_da(vps, attr_dhcp_opcode, TAG_ANY); if (vp) { *p++ = vp->vp_uint32 & 0xff; } else { *p++ = 1; /* client message */ } /* DHCP-Hardware-Type */ vp = fr_pair_find_by_da(vps, attr_dhcp_hardware_type, TAG_ANY); if (vp) { *p = vp->vp_uint8; } else if (original) { *p = original->htype; } /* else leave it unset */ p += 1; /* DHCP-Hardware-Address-len */ vp = fr_pair_find_by_da(vps, attr_dhcp_hardware_address_length, TAG_ANY); if (vp) { *p = vp->vp_uint8; } else if (original) { *p = original->hlen; } /* else leave it unset */ p += 1; /* DHCP-Hop-Count */ vp = fr_pair_find_by_da(vps, attr_dhcp_hop_count, TAG_ANY); if (vp) { *p = vp->vp_uint8; } else if (original) { *p = original->hops; } /* else leave it unset */ p++; /* DHCP-Transaction-Id */ lvalue = htonl(xid); memcpy(p, &lvalue, 4); p += 4; /* DHCP-Number-of-Seconds */ vp = fr_pair_find_by_da(vps, attr_dhcp_number_of_seconds, TAG_ANY); if (vp) { svalue = htons(vp->vp_uint16); memcpy(p, &svalue, 2); } p += 2; /* DHCP-Flags */ vp = fr_pair_find_by_da(vps, attr_dhcp_flags, TAG_ANY); if (vp) { svalue = htons(vp->vp_uint16); memcpy(p, &svalue, 2); } p += 2; /* DHCP-Client-IP-Address */ vp = fr_pair_find_by_da(vps, attr_dhcp_client_ip_address, TAG_ANY); if (vp) memcpy(p, &vp->vp_ipv4addr, 4); p += 4; /* DHCP-Your-IP-address */ vp = fr_pair_find_by_da(vps, attr_dhcp_your_ip_address, TAG_ANY); if (vp) { lvalue = vp->vp_ipv4addr; } else { lvalue = htonl(INADDR_ANY); } memcpy(p, &lvalue, 4); p += 4; /* DHCP-Server-IP-Address */ vp = fr_pair_find_by_da(vps, attr_dhcp_server_ip_address, TAG_ANY); if (vp) { lvalue = vp->vp_ipv4addr; } else { lvalue = htonl(INADDR_ANY); } memcpy(p, &lvalue, 4); p += 4; /* * DHCP-Gateway-IP-Address */ vp = fr_pair_find_by_da(vps, attr_dhcp_gateway_ip_address, TAG_ANY); if (vp) { lvalue = vp->vp_ipv4addr; memcpy(p, &lvalue, 4); } else if (original) { /* copy whatever value was in the original */ memcpy(p, &original->giaddr, sizeof(original->giaddr)); } else { lvalue = htonl(INADDR_ANY); memcpy(p, &lvalue, 4); } p += 4; /* DHCP-Client-Hardware-Address */ if ((vp = fr_pair_find_by_da(vps, attr_dhcp_client_hardware_address, TAG_ANY))) { if (vp->vp_type == FR_TYPE_ETHERNET) { /* * Ensure that we mark the packet as being Ethernet. */ buffer[1] = 1; /* Hardware address type = Ethernet */ buffer[2] = 6; /* Hardware address length = 6 */ memcpy(p, vp->vp_ether, sizeof(vp->vp_ether)); } /* else ignore it */ } else if (original) { /* copy whatever value was in the original */ memcpy(p, &original->chaddr[0], sizeof(original->chaddr)); } p += DHCP_CHADDR_LEN; /* DHCP-Server-Host-Name */ if ((vp = fr_pair_find_by_da(vps, attr_dhcp_server_host_name, TAG_ANY))) { if (vp->vp_length > DHCP_SNAME_LEN) { memcpy(p, vp->vp_strvalue, DHCP_SNAME_LEN); } else { memcpy(p, vp->vp_strvalue, vp->vp_length); } } p += DHCP_SNAME_LEN; /* * Copy over DHCP-Boot-Filename. * * FIXME: This copy should be delayed until AFTER the options * have been processed. If there are too many options for * the packet, then they go into the sname && filename fields. * When that happens, the boot filename is passed as an option, * instead of being placed verbatim in the filename field. */ /* DHCP-Boot-Filename */ vp = fr_pair_find_by_da(vps, attr_dhcp_boot_filename, TAG_ANY); if (vp) { if (vp->vp_length > DHCP_FILE_LEN) { memcpy(p, vp->vp_strvalue, DHCP_FILE_LEN); } else { memcpy(p, vp->vp_strvalue, vp->vp_length); } } p += DHCP_FILE_LEN; /* DHCP magic number */ lvalue = htonl(DHCP_OPTION_MAGIC_NUMBER); memcpy(p, &lvalue, 4); p += 4; p[0] = FR_DHCP_MESSAGE_TYPE; p[1] = 1; p[2] = code; p += 3; /* * Pre-sort attributes into contiguous blocks so that fr_dhcpv4_encode_option * operates correctly. This changes the order of the list, but never mind... */ fr_pair_list_sort(&vps, fr_dhcpv4_attr_cmp); fr_cursor_init(&cursor, &vps); /* * Each call to fr_dhcpv4_encode_option will encode one complete DHCP option, * and sub options. */ while ((vp = fr_cursor_current(&cursor))) { len = fr_dhcpv4_encode_option(p, buflen - (p - buffer), &cursor, &(fr_dhcpv4_ctx_t){ .root = fr_dict_root(dict_dhcpv4) }); if (len < 0) break; p += len; };