static int dhcprelay_process_client_request(REQUEST *request) { int rcode; uint8_t maxhops = 16; VALUE_PAIR *vp, *giaddr; dhcp_socket_t *sock; RADIUS_PACKET *packet; rad_assert(request->packet->data[0] == 1); /* * Do the forward by ourselves, do not rely on dhcp_socket_send() */ request->reply->code = 0; /* * It's invalid to have giaddr=0 AND a relay option */ giaddr = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 266, TAG_ANY); /* DHCP-Gateway-IP-Address */ if (giaddr && (giaddr->vp_ipv4addr == htonl(INADDR_ANY)) && fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 82, TAG_ANY)) { /* DHCP-Relay-Agent-Information */ RDEBUG2("Received packet with giaddr = 0 and containing relay option: Discarding packet"); return 1; } /* * RFC 1542 (BOOTP), page 15 * * Drop requests if hop-count > 16 or admin specified another value */ if ((vp = fr_pair_find_by_num(request->control, DHCP_MAGIC_VENDOR, 271, TAG_ANY))) { /* DHCP-Relay-Max-Hop-Count */ maxhops = vp->vp_uint32; } vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 259, TAG_ANY); /* DHCP-Hop-Count */ rad_assert(vp != NULL); if (vp->vp_uint8 > maxhops) { RDEBUG2("Number of hops is greater than %d: not relaying", maxhops); return 1; } else { /* Increment hop count */ vp->vp_uint8++; } sock = request->listener->data; /* * Don't muck with the original request packet. That's * bad form. Plus, dhcp_encode() does nothing if * packet->data is already set. */ packet = fr_radius_alloc(request, false); rcode = -1; /* * Forward the request to the next server using the * incoming request as a template. */ packet->code = request->packet->code; packet->sockfd = request->packet->sockfd; /* * Forward the request to the next server using the * incoming request as a template. */ /* set SRC ipaddr/port to the listener ipaddr/port */ packet->src_ipaddr.af = AF_INET; packet->src_ipaddr.addr.v4.s_addr = sock->lsock.my_ipaddr.addr.v4.s_addr; packet->src_port = sock->lsock.my_port; vp = fr_pair_find_by_num(request->control, DHCP_MAGIC_VENDOR, 270, TAG_ANY); /* DHCP-Relay-To-IP-Address */ rad_assert(vp != NULL); /* set DEST ipaddr/port to the next server ipaddr/port */ packet->dst_ipaddr.af = AF_INET; packet->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; packet->dst_port = sock->lsock.my_port; packet->vps = request->packet->vps; /* hackity hack */ /* * Relaying is not proxying, we just forward it on and forget * about it, not sending a response to the DHCP client. */ dhcp_packet_debug(request, packet, false); if (fr_dhcpv4_packet_encode(packet) < 0) { RPERROR("Failed encoding DHCP packet"); goto error; } rcode = fr_dhcpv4_udp_packet_send(packet); error: packet->vps = NULL; talloc_free(packet); return rcode; }
int main(int argc, char **argv) { static uint16_t server_port = 0; static int packet_code = 0; static fr_ipaddr_t server_ipaddr; static fr_ipaddr_t client_ipaddr; int c; char const *raddb_dir = RADDBDIR; char const *dict_dir = DICTDIR; char const *filename = NULL; RADIUS_PACKET *request = NULL; RADIUS_PACKET *reply = NULL; TALLOC_CTX *autofree = talloc_autofree_context(); int ret; fr_debug_lvl = 1; fr_log_fp = stdout; while ((c = getopt(argc, argv, "d:D:f:hr:t:vxi:")) != -1) switch(c) { case 'D': dict_dir = optarg; break; case 'd': raddb_dir = optarg; break; case 'f': filename = optarg; break; case 'i': iface = optarg; break; case 'r': if (!isdigit((int) *optarg)) usage(); retries = atoi(optarg); if ((retries == 0) || (retries > 1000)) usage(); break; case 't': if (!isdigit((int) *optarg)) usage(); timeout = atof(optarg); break; case 'v': DEBUG("%s", dhcpclient_version); exit(0); case 'x': fr_debug_lvl++; break; case 'h': default: usage(); } argc -= (optind - 1); argv += (optind - 1); if (argc < 2) usage(); /* convert timeout to a struct timeval */ #define USEC 1000000 tv_timeout.tv_sec = timeout; tv_timeout.tv_usec = ((timeout - (float) tv_timeout.tv_sec) * USEC); if (fr_dict_global_init(autofree, dict_dir) < 0) { fr_perror("dhcpclient"); exit(EXIT_FAILURE); } if (fr_dict_autoload(dhcpclient_dict) < 0) { fr_perror("dhcpclient"); exit(EXIT_FAILURE); } if (fr_dict_attr_autoload(dhcpclient_dict_attr) < 0) { fr_perror("dhcpclient"); exit(EXIT_FAILURE); } if (fr_dict_read(dict_freeradius, raddb_dir, FR_DICTIONARY_FILE) == -1) { fr_perror("dhcpclient"); exit(EXIT_FAILURE); } fr_strerror(); /* Clear the error buffer */ /* * Initialise the DHCPv4 library */ fr_dhcpv4_global_init(); /* * Resolve hostname. */ server_ipaddr.af = AF_INET; if (strcmp(argv[1], "-") != 0) { if (fr_inet_pton_port(&server_ipaddr, &server_port, argv[1], strlen(argv[1]), AF_UNSPEC, true, true) < 0) { fr_perror("dhcpclient"); fr_exit_now(1); } client_ipaddr.af = server_ipaddr.af; } /* * See what kind of request we want to send. */ if (argc >= 3) { if (!isdigit((int) argv[2][0])) { packet_code = fr_str2int(request_types, argv[2], -2); if (packet_code == -2) { ERROR("Unknown packet type: %s", argv[2]); usage(); } } else { packet_code = atoi(argv[2]); } } if (!server_port) server_port = 67; /* * set "raw mode" if an interface is specified and if destination * IP address is the broadcast address. */ if (iface) { iface_ind = if_nametoindex(iface); if (iface_ind <= 0) { ERROR("Unknown interface: %s", iface); exit(EXIT_FAILURE); } if (server_ipaddr.addr.v4.s_addr == 0xFFFFFFFF) { ERROR("Using interface: %s (index: %d) in raw packet mode", iface, iface_ind); raw_mode = true; } } request = request_init(filename); if (!request || !request->vps) { ERROR("Nothing to send"); exit(EXIT_FAILURE); } /* * Set defaults if they weren't specified via pairs */ if (request->src_port == 0) request->src_port = server_port + 1; if (request->dst_port == 0) request->dst_port = server_port; if (request->src_ipaddr.af == AF_UNSPEC) request->src_ipaddr = client_ipaddr; if (request->dst_ipaddr.af == AF_UNSPEC) request->dst_ipaddr = server_ipaddr; if (!request->code) request->code = packet_code; /* * Sanity check. */ if (!request->code) { ERROR("Command was %s, and request did not contain DHCP-Message-Type nor Packet-Type", (argc >= 3) ? "'auto'" : "unspecified"); exit(EXIT_FAILURE); } /* * These kind of packets do not get a reply, so don't wait for one. */ if ((request->code == FR_DHCP_RELEASE) || (request->code == FR_DHCP_DECLINE)) { reply_expected = false; } /* * Encode the packet */ if (fr_dhcpv4_packet_encode(request) < 0) { ERROR("Failed encoding packet"); exit(EXIT_FAILURE); } /* * Decode to produce VALUE_PAIRs from the default field */ if (fr_debug_lvl) { fr_dhcpv4_packet_decode(request); dhcp_packet_debug(request, false); } #ifdef HAVE_LIBPCAP if (raw_mode) { ret = send_with_pcap(&reply, request); } else #endif { ret = send_with_socket(&reply, request); } if (reply) { if (fr_dhcpv4_packet_decode(reply) < 0) { ERROR("Failed decoding packet"); ret = -1; } dhcp_packet_debug(reply, true); } fr_dhcpv4_global_free(); fr_dict_autofree(dhcpclient_dict); return ret < 0 ? 1 : 0; }
/* * We've seen a reply from a server. * i.e. we're a relay. */ static int dhcprelay_process_server_reply(REQUEST *request) { int rcode; VALUE_PAIR *vp, *giaddr; dhcp_socket_t *sock; RADIUS_PACKET *packet; rad_assert(request->packet->data[0] == 2); /* * Do the forward by ourselves, do not rely on dhcp_socket_send() */ request->reply->code = 0; sock = request->listener->data; /* * Check that packet is for us. */ giaddr = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 266, TAG_ANY); /* DHCP-Gateway-IP-Address */ /* --with-udpfromto is needed just for the following test */ if (!giaddr || giaddr->vp_ipv4addr != request->packet->dst_ipaddr.addr.v4.s_addr) { RDEBUG2("Packet received from server was not for us (was for 0x%x). Discarding packet", ntohl(request->packet->dst_ipaddr.addr.v4.s_addr)); return 1; } /* * Don't muck with the original request packet. That's * bad form. Plus, dhcp_encode() does nothing if * packet->data is already set. */ packet = fr_radius_alloc(request, false); rcode = -1; /* * Forward the request to the next server using the * incoming request as a template. */ packet->code = request->packet->code; packet->sockfd = request->packet->sockfd; /* set SRC ipaddr/port to the listener ipaddr/port */ packet->src_ipaddr.af = AF_INET; packet->src_port = sock->lsock.my_port; /* set DEST ipaddr/port to clientip/68 or broadcast in specific cases */ packet->dst_ipaddr.af = AF_INET; /* * We're a relay, and send the reply to giaddr. */ packet->dst_ipaddr.addr.v4.s_addr = htonl(INADDR_BROADCAST); packet->dst_port = request->packet->dst_port; /* server port */ vp = fr_pair_find_by_num(request->control, DHCP_MAGIC_VENDOR, 270, TAG_ANY); /* DHCP-Relay-To-IP-Address */ if (vp) { RDEBUG("DHCP: response will be relayed to previous gateway"); packet->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; giaddr->vp_ipv4addr = vp->vp_ipv4addr; } else if ((packet->code == FR_DHCP_NAK) || !sock->src_interface || ((vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 262, TAG_ANY)) /* DHCP-Flags */ && (vp->vp_uint32 & 0x8000) && ((vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 263, TAG_ANY)) /* DHCP-Client-IP-Address */ && (vp->vp_ipv4addr == htonl(INADDR_ANY))))) { /* * RFC 2131, page 23 * * Broadcast on * - DHCPNAK * or * - Broadcast flag is set up and ciaddr == NULL */ RDEBUG2("Response will be broadcast"); packet->dst_ipaddr.addr.v4.s_addr = htonl(INADDR_BROADCAST); } else if ((vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 263, TAG_ANY)) /* DHCP-Client-IP-Address */ && (vp->vp_ipv4addr != htonl(INADDR_ANY))) { /* * RFC 2131, page 23 * * Unicast to * - ciaddr if present * otherwise to yiaddr */ packet->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; } else { vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 264, TAG_ANY); /* DHCP-Your-IP-Address */ if (!vp) { RPEDEBUG("Failed to find IP Address for request"); goto error; } RDEBUG2("Response will be unicast to &DHCP-Your-IP-Address"); packet->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; /* * When sending a DHCP_OFFER, make sure our ARP table * contains an entry for the client IP address, or else * packet may not be forwarded if it was the first time * the client was requesting an IP address. */ if (request->packet->code == FR_DHCP_OFFER) { VALUE_PAIR *hwvp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 267, TAG_ANY); /* DHCP-Client-Hardware-Address */ if (hwvp == NULL) { RDEBUG2("DHCP_OFFER packet received with no Client Hardware Address. " "Discarding packet"); goto error; } if (fr_dhcpv4_udp_add_arp_entry(request->packet->sockfd, sock->src_interface, &vp->vp_ip, hwvp->vp_ether) < 0) { REDEBUG("Failed adding ARP entry"); goto error; } } } packet->vps = request->packet->vps; /* hackity hack */ /* * Our response doesn't go through process.c */ dhcp_packet_debug(request, packet, false); if (fr_dhcpv4_packet_encode(packet) < 0) { RPERROR("Failed encoding DHCP packet"); goto error; } rcode = fr_dhcpv4_udp_packet_send(request->packet); error: packet->vps = NULL; talloc_free(packet); return rcode; }