Beispiel #1
0
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;
}
Beispiel #2
0
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;
}
Beispiel #3
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;
}