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; }
static int send_with_socket(RADIUS_PACKET **reply, RADIUS_PACKET *request) { int on = 1; #ifdef HAVE_LINUX_IF_PACKET_H if (raw_mode) { sockfd = fr_dhcpv4_raw_socket_open(&ll, iface_ind); if (sockfd < 0) { ERROR("Error opening socket"); return -1; } } else #endif { sockfd = fr_socket_server_udp(&request->src_ipaddr, &request->src_port, NULL, false); if (sockfd < 0) { ERROR("Error opening socket: %s", fr_strerror()); return -1; } if (fr_socket_bind(sockfd, &request->src_ipaddr, &request->src_port, NULL) < 0) { ERROR("Error binding socket: %s", fr_strerror()); return -1; } } /* * Set option 'receive timeout' on socket. * Note: in case of a timeout, the error will be "Resource temporarily unavailable". */ if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv_timeout, sizeof(struct timeval)) == -1) { ERROR("Failed setting socket timeout: %s", fr_syserror(errno)); return -1; } if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) { ERROR("Can't set broadcast option: %s", fr_syserror(errno)); return -1; } request->sockfd = sockfd; #ifdef HAVE_LINUX_IF_PACKET_H if (raw_mode) { if (fr_dhcpv4_raw_packet_send(sockfd, &ll, request) < 0) { ERROR("Failed sending (fr_dhcpv4_raw_packet_send): %s", fr_syserror(errno)); return -1; } if (!reply_expected) return 0; *reply = fr_dhcpv4_recv_raw_loop(sockfd, &ll, request); if (!*reply) { ERROR("Error receiving reply (fr_dhcpv4_recv_raw_loop)"); return -1; } } else #endif { if (fr_dhcpv4_udp_packet_send(request) < 0) { ERROR("Failed sending: %s", fr_syserror(errno)); return -1; } if (!reply_expected) return 0; *reply = fr_dhcpv4_udp_packet_recv(sockfd); if (!*reply) { if (errno == EAGAIN) { fr_strerror(); /* clear error */ ERROR("Timed out waiting for reply"); } else { ERROR("Error receiving reply"); } return -1; } } return 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; }