#ifdef WITH_DHCP
typedef struct dhcp_socket_t {
fr_ipaddr_t ipaddr;
int port;
const char *interface;
RADCLIENT_LIST *clients;
int suppress_responses;
RADCLIENT dhcp_client;
} dhcp_socket_t;
static int dhcp_process(REQUEST *request)
{
int rcode;
VALUE_PAIR *vp;
vp = pairfind(request->packet->vps, DHCP2ATTR(53));
if (vp) {
DICT_VALUE *dv = dict_valbyattr(DHCP2ATTR(53), vp->vp_integer);
DEBUG("Trying sub-section dhcp %s {...}",
dv->name ? dv->name : "<unknown>");
rcode = module_post_auth(vp->vp_integer, request);
} else {
DEBUG("DHCP: Failed to find DHCP-Message-Type in packet!");
rcode = RLM_MODULE_FAIL;
}
vp = NULL;
if (request->packet->data[0] == 1) {
vp = pairfind(request->config_items, DHCP2ATTR(270));
}
if (vp) {
VALUE_PAIR *giaddr;
giaddr = pairfind(request->packet->vps, DHCP2ATTR(266));
if (giaddr && (giaddr->vp_ipaddr == htonl(INADDR_ANY))) {
if (pairfind(request->packet->vps, DHCP2ATTR(82))) {
RDEBUG("DHCP: Received packet with giaddr = 0 and containing relay option: Discarding packet");
return 1;
}
}
if (request->packet->data[3] > 10) {
RDEBUG("DHCP: Number of hops is greater than 10: not relaying");
return 1;
}
pairfree(&request->reply->vps);
request->reply->vps = paircopy(request->packet->vps);
request->reply->code = request->packet->code;
request->reply->id = request->packet->id;
request->reply->src_ipaddr = request->packet->dst_ipaddr;
request->reply->src_port = request->packet->dst_port;
request->reply->dst_ipaddr.af = AF_INET;
request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
vp = pairfind(request->reply->vps, DHCP2ATTR(259));
if (vp) vp->vp_integer++;
return 1;
}
if (request->packet->data[0] == 2) {
pairfree(&request->reply->vps);
request->reply->vps = paircopy(request->packet->vps);
request->reply->code = request->packet->code;
request->reply->id = request->packet->id;
pairdelete(&request->reply->vps, DHCP2ATTR(266));
vp = pairfind(request->reply->vps, DHCP2ATTR(264));
if (!vp) {
request->reply->code = 0;
RDEBUG("DHCP: No YIAddr in the reply. Discarding packet");
return 1;
}
request->reply->src_ipaddr = request->packet->dst_ipaddr;
request->reply->src_port = request->packet->dst_port;
request->reply->dst_ipaddr.af = AF_INET;
request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
request->reply->dst_port = request->packet->dst_port + 1;
vp = pairfind(request->reply->vps, DHCP2ATTR(259));
if (vp && (vp->vp_integer > 0)) vp->vp_integer--;
return 1;
}
vp = pairfind(request->reply->vps, DHCP2ATTR(53));
if (vp) {
request->reply->code = vp->vp_integer;
if ((request->reply->code != 0) &&
(request->reply->code < PW_DHCP_OFFSET)) {
request->reply->code += PW_DHCP_OFFSET;
}
}
else switch (rcode) {
case RLM_MODULE_OK:
case RLM_MODULE_UPDATED:
if (request->packet->code == PW_DHCP_DISCOVER) {
request->reply->code = PW_DHCP_OFFER;
break;
} else if (request->packet->code == PW_DHCP_REQUEST) {
request->reply->code = PW_DHCP_ACK;
break;
}
request->reply->code = PW_DHCP_NAK;
break;
default:
case RLM_MODULE_REJECT:
case RLM_MODULE_FAIL:
case RLM_MODULE_INVALID:
case RLM_MODULE_NOOP:
case RLM_MODULE_NOTFOUND:
if (request->packet->code == PW_DHCP_DISCOVER) {
request->reply->code = 0;
} else {
request->reply->code = PW_DHCP_NAK;
}
break;
case RLM_MODULE_HANDLED:
break;
}
if (request->packet->code == PW_DHCP_RELEASE) {
request->reply->code = 0;
}
return 1;
}
static int dhcp_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
{
int rcode, broadcast = 1;
int on = 1;
dhcp_socket_t *sock;
RADCLIENT *client;
CONF_PAIR *cp;
rcode = common_socket_parse(cs, this);
if (rcode != 0) return rcode;
if (check_config) return 0;
sock = this->data;
cp = cf_pair_find(cs, "broadcast");
if (cp) {
const char *value = cf_pair_value(cp);
if (value && (strcmp(value, "no") == 0)) {
broadcast = 0;
}
}
if (broadcast) {
if (setsockopt(this->fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
radlog(L_ERR, "Can't set broadcast option: %s\n",
strerror(errno));
return -1;
}
}
if (setsockopt(this->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
radlog(L_ERR, "Can't set re-use addres option: %s\n",
strerror(errno));
return -1;
}
sock->suppress_responses = FALSE;
cp = cf_pair_find(cs, "suppress_responses");
if (cp) {
const char *value;
value = cf_pair_value(cp);
if (value && (strcmp(value, "yes") == 0)) {
sock->suppress_responses = TRUE;
}
}
client = &sock->dhcp_client;
memset(client, 0, sizeof(*client));
client->ipaddr.af = AF_INET;
client->ipaddr.ipaddr.ip4addr.s_addr = INADDR_NONE;
client->prefix = 0;
client->longname = client->shortname = "dhcp";
client->secret = client->shortname;
client->nastype = strdup("none");
return 0;
}
static int dhcp_socket_recv(rad_listen_t *listener,
RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
{
RADIUS_PACKET *packet;
dhcp_socket_t *sock;
packet = fr_dhcp_recv(listener->fd);
if (!packet) {
radlog(L_ERR, "%s", fr_strerror());
return 0;
}
sock = listener->data;
if (!received_request(listener, packet, prequest, &sock->dhcp_client)) {
rad_free(&packet);
return 0;
}
*pfun = dhcp_process;
return 1;
}
static int dhcp_socket_send(rad_listen_t *listener, REQUEST *request)
{
dhcp_socket_t *sock;
rad_assert(request->listener == listener);
rad_assert(listener->send == dhcp_socket_send);
if (request->reply->code == 0) return 0;
if (request->packet->code != request->reply->code) {
if (fr_dhcp_encode(request->reply, request->packet) < 0) {
return -1;
}
} else {
if (fr_dhcp_encode(request->reply, NULL) < 0) {
return -1;
}
}
sock = listener->data;
if (sock->suppress_responses) return 0;
return fr_dhcp_send(request->reply);
}
static int dhcp_socket_encode(UNUSED rad_listen_t *listener, REQUEST *request)
{
DEBUG2("NO ENCODE!");
return 0;
return fr_dhcp_encode(request->reply, request->packet);
}
static int dhcp_socket_decode(UNUSED rad_listen_t *listener, REQUEST *request)
{
return fr_dhcp_decode(request->packet);
}
#endif