* Copyright 2008 Alan DeKok <aland@deployingradius.com>
*/
+#include <sys/ioctl.h>
+#include <net/if_arp.h>
+
#include <freeradius-devel/ident.h>
RCSID("$Id$")
#include <freeradius-devel/udpfromto.h>
#include <freeradius-devel/dhcp.h>
-/*
- * This doesn't appear to work right now.
- */
-#undef WITH_UDPFROMTO
-
#ifdef WITH_DHCP
+
#define DHCP_CHADDR_LEN (16)
#define DHCP_SNAME_LEN (64)
#define DHCP_FILE_LEN (128)
static uint8_t *dhcp_get_option(dhcp_packet_t *packet, size_t packet_size,
unsigned int option)
-
{
int overload = 0;
int field = DHCP_OPTION_FIELD;
*/
getsockname(sockfd, (struct sockaddr *) &dst, &sizeof_dst);
#endif
-
+
fr_sockaddr2ipaddr(&dst, sizeof_dst, &packet->dst_ipaddr, &port);
packet->dst_port = port;
char type_buf[64];
const char *name = type_buf;
char src_ip_buf[256], dst_ip_buf[256];
-
+
if ((packet->code >= PW_DHCP_DISCOVER) &&
(packet->code <= PW_DHCP_INFORM)) {
name = dhcp_message_types[packet->code - PW_DHCP_OFFSET];
#ifdef WITH_UDPFROMTO
struct sockaddr_storage src;
socklen_t sizeof_src;
+
+ fr_ipaddr2sockaddr(&packet->src_ipaddr, packet->src_port,
+ &src, &sizeof_src);
#endif
fr_ipaddr2sockaddr(&packet->dst_ipaddr, packet->dst_port,
&dst, &sizeof_dst);
- /*
- * The client doesn't yet have an IP address, but is
- * expecting an ethernet packet unicast to it's MAC
- * address. We need to build a raw frame.
- */
- if (packet->offset == 0) {
- /*
- * FIXME: Insert code here!
- */
+ if (fr_debug_flag > 1) {
+ char type_buf[64];
+ const char *name = type_buf;
+#ifdef WITH_UDPFROMTO
+ char src_ip_buf[256];
+#endif
+ char dst_ip_buf[256];
+
+ if ((packet->code >= PW_DHCP_DISCOVER) &&
+ (packet->code <= PW_DHCP_INFORM)) {
+ name = dhcp_message_types[packet->code - PW_DHCP_OFFSET];
+ } else {
+ snprintf(type_buf, sizeof(type_buf), "%d",
+ packet->code - PW_DHCP_OFFSET);
+ }
+
+ DEBUG(
+#ifdef WITH_UDPFROMTO
+ "Sending %s of id %08x from %s:%d to %s:%d\n",
+#else
+ "Sending %s of id %08x to %s:%d\n",
+#endif
+ name, (unsigned int) packet->id,
+#ifdef WITH_UDPFROMTO
+ inet_ntop(packet->src_ipaddr.af,
+ &packet->src_ipaddr.ipaddr,
+ src_ip_buf, sizeof(src_ip_buf)),
+ packet->src_port,
+#endif
+ inet_ntop(packet->dst_ipaddr.af,
+ &packet->dst_ipaddr.ipaddr,
+ dst_ip_buf, sizeof(dst_ip_buf)),
+ packet->dst_port);
}
#ifndef WITH_UDPFROMTO
* Assume that the packet is encoded before sending it.
*/
return sendto(packet->sockfd, packet->data, packet->data_len, 0,
- (struct sockaddr *)&dst, sizeof_dst);
+ (struct sockaddr *)&dst, sizeof_dst);
#else
- fr_ipaddr2sockaddr(&packet->src_ipaddr, packet->src_port,
- &src, &sizeof_src);
- return sendfromto(packet->sockfd,
- packet->data, packet->data_len, 0,
- (struct sockaddr *)&src, sizeof_src,
- (struct sockaddr *)&dst, sizeof_dst);
+ return sendfromto(packet->sockfd, packet->data, packet->data_len, 0,
+ (struct sockaddr *)&src, sizeof_src,
+ (struct sockaddr *)&dst, sizeof_dst);
#endif
}
}
memcpy(tlv->vp_tlv, data, data_len);
tlv->length = data_len;
-
+
return 0;
}
if (alen != 1) goto raw;
vp->vp_integer = p[0];
break;
-
+
case PW_TYPE_SHORT:
if (alen != 2) goto raw;
- vp->vp_integer = (p[0] << 8) | p[1];
+ memcpy(&vp->vp_integer, p, 2);
+ vp->vp_integer = ntohs(vp->vp_integer);
break;
-
+
case PW_TYPE_INTEGER:
if (alen != 4) goto raw;
memcpy(&vp->vp_integer, p, 4);
vp->vp_integer = ntohl(vp->vp_integer);
break;
-
+
case PW_TYPE_IPADDR:
if (alen != 4) goto raw;
- memcpy(&vp->vp_ipaddr, p , 4);
+ memcpy(&vp->vp_ipaddr, p , 4); /* Keep value in Network Order!!! */
vp->length = 4;
break;
-
+
case PW_TYPE_STRING:
if (alen > 253) return -1;
memcpy(vp->vp_strvalue, p , alen);
vp->vp_strvalue[alen] = '\0';
break;
-
+
raw:
vp->type = PW_TYPE_OCTETS;
if (alen > 253) return -1;
memcpy(vp->vp_octets, p, alen);
break;
-
+
case PW_TYPE_TLV:
return decode_tlv(vp, p, alen);
-
+
default:
fr_strerror_printf("Internal sanity check %d %d", vp->type, __LINE__);
return -1;
head = NULL;
tail = &head;
p = packet->data;
-
+
if ((fr_debug_flag > 2) && fr_log_fp) {
for (i = 0; i < packet->data_len; i++) {
if ((i & 0x0f) == 0x00) fr_strerror_printf("%d: ", i);
return -1;
}
- if ((i == 11) &&
+ if ((i == 11) &&
(packet->data[1] == 1) &&
(packet->data[2] == 6)) {
vp->type = PW_TYPE_ETHERNET;
vp->vp_integer = p[0];
vp->length = 1;
break;
-
+
case PW_TYPE_SHORT:
vp->vp_integer = (p[0] << 8) | p[1];
vp->length = 2;
break;
-
+
case PW_TYPE_INTEGER:
memcpy(&vp->vp_integer, p, 4);
vp->vp_integer = ntohl(vp->vp_integer);
vp->length = 4;
break;
-
+
case PW_TYPE_IPADDR:
memcpy(&vp->vp_ipaddr, p, 4);
vp->length = 4;
break;
-
+
case PW_TYPE_STRING:
memcpy(vp->vp_strvalue, p, dhcp_header_sizes[i]);
vp->vp_strvalue[dhcp_header_sizes[i]] = '\0';
pairfree(&vp);
}
break;
-
+
case PW_TYPE_OCTETS:
memcpy(vp->vp_octets, p, packet->data[2]);
vp->length = packet->data[2];
break;
-
+
case PW_TYPE_ETHERNET:
memcpy(vp->vp_ether, p, sizeof(vp->vp_ether));
vp->length = sizeof(vp->vp_ether);
break;
-
+
default:
fr_strerror_printf("BAD TYPE %d", vp->type);
pairfree(&vp);
p += dhcp_header_sizes[i];
if (!vp) continue;
-
+
debug_pair(vp);
*tail = vp;
tail = &vp->next;
}
-
+
/*
* Loop over the options.
*/
while (next < (packet->data + packet->data_len)) {
int num_entries, alen;
DICT_ATTR *da;
-
+
p = next;
if (*p == 0) break;
p[0], p[1]);
continue;
}
-
+
da = dict_attrbyvalue(DHCP2ATTR(p[0]));
if (!da) {
fr_strerror_printf("Attribute not in our dictionary: %u",
* Reply should be broadcast.
*/
if (vp) vp->lvalue |= 0x8000;
- packet->data[10] |= 0x80;
+ packet->data[10] |= 0x80;
}
}
}
if (fr_debug_flag > 0) {
for (vp = packet->vps; vp != NULL; vp = vp->next) {
-
+
}
}
length = 1;
*p = vp->vp_integer & 0xff;
break;
-
+
case PW_TYPE_SHORT:
length = 2;
- p[0] = (vp->vp_integer >> 8) & 0xff;
- p[1] = vp->vp_integer & 0xff;
+ lvalue = htons(vp->vp_integer);
+ memcpy(p, &lvalue, 2);
break;
-
+
case PW_TYPE_INTEGER:
length = 4;
lvalue = htonl(vp->vp_integer);
memcpy(p, &lvalue, 4);
break;
-
+
case PW_TYPE_IPADDR:
length = 4;
memcpy(p, &vp->vp_ipaddr, 4);
break;
-
+
case PW_TYPE_ETHERNET:
length = 6;
memcpy(p, &vp->vp_ether, 6);
break;
-
+
case PW_TYPE_STRING:
memcpy(p, vp->vp_strvalue, vp->length);
length = vp->length;
break;
-
+
case PW_TYPE_TLV: /* FIXME: split it on 255? */
memcpy(p, vp->vp_tlv, vp->length);
length = vp->length;
memcpy(p, vp->vp_octets, vp->length);
length = vp->length;
break;
-
+
default:
fr_strerror_printf("BAD TYPE2 %d", vp->type);
length = 0;
return tlv;
}
-
-int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
+int fr_dhcp_encode(RADIUS_PACKET *packet)
{
int i, num_vps;
uint8_t *p;
VALUE_PAIR *vp;
uint32_t lvalue, mms;
size_t dhcp_size, length;
- dhcp_packet_t *dhcp;
char buffer[1024];
- if (packet->data) return 0;
+ if (packet->data) free(packet->data);
packet->data = malloc(MAX_PACKET_SIZE);
- if (!packet->data) return -1;
-
packet->data_len = MAX_PACKET_SIZE;
+ memset(packet->data, 0, packet->data_len);
+ /* XXX Ugly ... should be set by the caller */
if (packet->code == 0) packet->code = PW_DHCP_NAK;
- /*
- * If there's a request, use it as a template.
- * Otherwise, assume that the caller has set up
- * everything appropriately.
- */
- if (original) {
- packet->dst_ipaddr.af = AF_INET;
- packet->src_ipaddr.af = AF_INET;
-
- packet->dst_port = original->src_port;
- packet->src_port = original->dst_port;
-
- /*
- * Note that for DHCP, we NEVER send the response
- * to the source IP address of the request. It
- * may have traversed multiple relays, and we
- * need to send the request to the relay closest
- * to the client.
- *
- * if giaddr, send to giaddr.
- * if NAK, send broadcast.
- * if broadcast flag, send broadcast.
- * if ciaddr is empty, send broadcast.
- * otherwise unicast to ciaddr.
- */
-
- /*
- * FIXME: alignment issues. We likely don't want to
- * de-reference the packet structure directly..
- */
- dhcp = (dhcp_packet_t *) original->data;
-
- /*
- * Default to sending it via sendto(), without using
- * raw sockets.
- */
- packet->offset = 1;
-
- if (dhcp->giaddr != htonl(INADDR_ANY)) {
- packet->dst_ipaddr.ipaddr.ip4addr.s_addr = dhcp->giaddr;
-
- if (dhcp->giaddr != htonl(INADDR_LOOPBACK)) {
- packet->dst_port = original->dst_port;
- } else {
- packet->dst_port = original->src_port; /* debugging */
- }
-
- } else if ((packet->code == PW_DHCP_NAK) ||
- ((dhcp->flags & 0x8000) != 0) ||
- (dhcp->ciaddr == htonl(INADDR_ANY))) {
- /*
- * The kernel will take care of sending it to
- * the broadcast MAC.
- */
- packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST);
-
- } else {
- /*
- * It was broadcast to us: we need to
- * broadcast the response.
- */
- if (packet->src_ipaddr.ipaddr.ip4addr.s_addr != dhcp->ciaddr) {
- packet->offset = 0;
- }
- packet->dst_ipaddr.ipaddr.ip4addr.s_addr = dhcp->ciaddr;
- }
-
- /*
- * Rewrite the source IP to be our own, if we know it.
- */
- if (packet->src_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_BROADCAST)) {
- packet->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_ANY);
- }
- } else {
- memset(packet->data, 0, packet->data_len);
- }
-
- if (fr_debug_flag > 1) {
- char type_buf[64];
- const char *name = type_buf;
- char src_ip_buf[256], dst_ip_buf[256];
-
- if ((packet->code >= PW_DHCP_DISCOVER) &&
- (packet->code <= PW_DHCP_INFORM)) {
- name = dhcp_message_types[packet->code - PW_DHCP_OFFSET];
- } else {
- snprintf(type_buf, sizeof(type_buf), "%d",
- packet->code - PW_DHCP_OFFSET);
- }
-
- DEBUG("Sending %s of id %08x from %s:%d to %s:%d\n",
- name, (unsigned int) packet->id,
- inet_ntop(packet->src_ipaddr.af,
- &packet->src_ipaddr.ipaddr,
- src_ip_buf, sizeof(src_ip_buf)),
- packet->src_port,
- inet_ntop(packet->dst_ipaddr.af,
- &packet->dst_ipaddr.ipaddr,
- dst_ip_buf, sizeof(dst_ip_buf)),
- packet->dst_port);
-
- if (fr_debug_flag) {
- for (i = 256; i < 269; i++) {
- vp = pairfind(packet->vps, DHCP2ATTR(i));
- if (!vp) continue;
-
- debug_pair(vp);
- }
- }
- }
-
p = packet->data;
mms = DEFAULT_PACKET_SIZE; /* maximum message size */
- if (original) {
- /*
- * Clients can request a LARGER size, but not a
- * smaller one. They also cannot request a size
- * larger than MTU.
- */
- vp = pairfind(original->vps, DHCP2ATTR(57));
- if (vp && (vp->vp_integer > mms)) {
- mms = vp->vp_integer;
-
- if (mms > MAX_PACKET_SIZE) mms = MAX_PACKET_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 = pairfind(packet->vps, DHCP2ATTR(57));
+ if (vp && (vp->vp_integer > mms)) {
+ mms = vp->vp_integer;
+
+ if (mms > MAX_PACKET_SIZE) mms = MAX_PACKET_SIZE;
}
/*
}
} else { /* we don't support this type! */
fr_strerror_printf("DHCP-Authentication %d unsupported",
- vp->vp_octets[0]);
+ vp->vp_octets[0]);
}
}
- vp = pairfind(packet->vps, DHCP2ATTR(256));
- if (vp) {
+ /* DHCP-Opcode */
+ if ((vp = pairfind(packet->vps, DHCP2ATTR(256)))) {
*p++ = vp->vp_integer & 0xff;
} else {
- if (!original) {
- *p++ = 1; /* client message */
- } else {
- *p++ = 2; /* server message */
- }
+ *p++ = 1; /* client message */
+ }
+
+ /* DHCP-Hardware-Type */
+ if ((vp = pairfind(packet->vps, DHCP2ATTR(257)))) {
+ *p++ = vp->vp_integer & 0xFF;
+ } else {
+ *p++ = 1; /* hardware type = ethernet */
}
- *p++ = 1; /* hardware type = ethernet */
- *p++ = 6; /* 6 bytes of ethernet */
- vp = pairfind(packet->vps, DHCP2ATTR(259));
- if (vp) {
- *p++ = vp->vp_integer & 0xff;
+ /* DHCP-Hardware-Address-Length */
+ if ((vp = pairfind(packet->vps, DHCP2ATTR(258)))) {
+ *p++ = vp->vp_integer & 0xFF;
} else {
- *p++ = 0; /* hops */
+ *p++ = 6; /* 6 bytes of ethernet */
}
- if (original) { /* Xid */
- memcpy(p, original->data + 4, 4);
+ /* DHCP-Hop-Count */
+ if ((vp = pairfind(packet->vps, DHCP2ATTR(259)))) {
+ *p = vp->vp_integer & 0xff;
+ }
+ p++;
+
+ /* DHCP-Transaction-Id */
+ if ((vp = pairfind(packet->vps, DHCP2ATTR(260)))) {
+ lvalue = htonl(vp->vp_integer);
} else {
lvalue = fr_rand();
- memcpy(p, &lvalue, 4);
}
+ memcpy(p, &lvalue, 4);
p += 4;
- memset(p, 0, 2); /* secs are zero */
+ /* DHCP-Number-of-Seconds */
+ if ((vp = pairfind(packet->vps, DHCP2ATTR(261)))) {
+ lvalue = htonl(vp->vp_integer);
+ memcpy(p, &lvalue, 2);
+ }
p += 2;
- if (original) {
- memcpy(p, original->data + 10, 6); /* copy flags && ciaddr */
+ /* DHCP-Flags */
+ if ((vp = pairfind(packet->vps, DHCP2ATTR(262)))) {
+ lvalue = htons(vp->vp_integer);
+ memcpy(p, &lvalue, 2);
}
+ p += 2;
- /*
- * Allow the admin to set the broadcast flag.
- */
- vp = pairfind(packet->vps, DHCP2ATTR(262));
- if (vp) {
- p[0] |= (vp->vp_integer & 0xff00) >> 8;
- p[1] |= (vp->vp_integer & 0xff);
+ /* DHCP-Client-IP-Address */
+ if ((vp = pairfind(packet->vps, DHCP2ATTR(263)))) {
+ memcpy(p, &vp->vp_ipaddr, 4);
}
+ p += 4;
- p += 6;
-
- /*
- * Set client IP address.
- */
- vp = pairfind(packet->vps, DHCP2ATTR(264)); /* Your IP address */
- if (vp) {
+ /* DHCP-Your-IP-address */
+ if ((vp = pairfind(packet->vps, DHCP2ATTR(264)))) {
lvalue = vp->vp_ipaddr;
} else {
lvalue = htonl(INADDR_ANY);
}
- memcpy(p, &lvalue, 4); /* your IP address */
+ memcpy(p, &lvalue, 4);
p += 4;
- vp = pairfind(packet->vps, DHCP2ATTR(265)); /* server IP address */
- if (!vp) vp = pairfind(packet->vps, DHCP2ATTR(54)); /* identifier */
+ /* DHCP-Server-IP-Address */
+ vp = pairfind(packet->vps, DHCP2ATTR(265));
+
+ /* DHCP-DHCP-Server-Identifier */
+ if (!vp) vp = pairfind(packet->vps, DHCP2ATTR(54));
if (vp) {
lvalue = vp->vp_ipaddr;
} else {
lvalue = htonl(INADDR_ANY);
}
- memcpy(p, &lvalue, 4); /* Server IP address */
+ memcpy(p, &lvalue, 4);
p += 4;
- if (original) {
- memcpy(p, original->data + 24, 4); /* copy gateway IP address */
+ /* DHCP-Gateway-IP-Address */
+ if ((vp = pairfind(packet->vps, DHCP2ATTR(266)))) {
+ lvalue = vp->vp_ipaddr;
} else {
- vp = pairfind(packet->vps, DHCP2ATTR(266));
- if (vp) {
- lvalue = vp->vp_ipaddr;
- } else {
- lvalue = htonl(INADDR_NONE);
- }
- memcpy(p, &lvalue, 4);
+ lvalue = htonl(INADDR_ANY);
}
+ memcpy(p, &lvalue, 4);
p += 4;
- if (original) {
- memcpy(p, original->data + 28, DHCP_CHADDR_LEN);
- } else {
- vp = pairfind(packet->vps, DHCP2ATTR(267));
- if (vp) {
- if (vp->length > DHCP_CHADDR_LEN) {
- memcpy(p, vp->vp_octets, DHCP_CHADDR_LEN);
- } else {
- memcpy(p, vp->vp_octets, vp->length);
- }
+ /* DHCP-Client-Hardware-Address */
+ if ((vp = pairfind(packet->vps, DHCP2ATTR(267)))) {
+ if (vp->length > DHCP_CHADDR_LEN) {
+ memcpy(p, vp->vp_octets, DHCP_CHADDR_LEN);
+ } else {
+ memcpy(p, vp->vp_octets, vp->length);
}
}
p += DHCP_CHADDR_LEN;
- /*
- * Zero our sname && filename fields.
- */
- memset(p, 0, DHCP_SNAME_LEN + DHCP_FILE_LEN);
+ /* DHCP-Server-Host-Name */
+ if ((vp = pairfind(packet->vps, DHCP2ATTR(268)))) {
+ if (vp->length > DHCP_SNAME_LEN) {
+ memcpy(p, vp->vp_strvalue, DHCP_SNAME_LEN);
+ } else {
+ memcpy(p, vp->vp_strvalue, vp->length);
+ }
+ }
p += DHCP_SNAME_LEN;
/*
* When that happens, the boot filename is passed as an option,
* instead of being placed verbatim in the filename field.
*/
- vp = pairfind(packet->vps, DHCP2ATTR(269));
- if (vp) {
+
+ /* DHCP-Boot-Filename */
+ if ((vp = pairfind(packet->vps, DHCP2ATTR(269)))) {
if (vp->length > DHCP_FILE_LEN) {
memcpy(p, vp->vp_strvalue, DHCP_FILE_LEN);
} else {
}
p += DHCP_FILE_LEN;
- lvalue = htonl(DHCP_OPTION_MAGIC_NUMBER); /* DHCP magic number */
+ /* DHCP magic number */
+ lvalue = htonl(DHCP_OPTION_MAGIC_NUMBER);
memcpy(p, &lvalue, 4);
p += 4;
fr_strerror_printf("Parse error %s", fr_strerror());
return -1;
}
-
+
switch (vp->type) {
case PW_TYPE_BYTE:
vp->vp_integer = p[0];
vp->length = 1;
break;
-
+
case PW_TYPE_SHORT:
- vp->vp_integer = (p[0] << 8) | p[1];
+ memcpy(&vp->vp_integer, p, 2);
+ vp->vp_integer = ntohs(vp->vp_integer);
vp->length = 2;
break;
-
+
case PW_TYPE_INTEGER:
memcpy(&vp->vp_integer, p, 4);
vp->vp_integer = ntohl(vp->vp_integer);
vp->length = 4;
break;
-
+
case PW_TYPE_IPADDR:
memcpy(&vp->vp_ipaddr, p, 4);
vp->length = 4;
break;
-
+
case PW_TYPE_STRING:
memcpy(vp->vp_strvalue, p, dhcp_header_sizes[i]);
vp->vp_strvalue[dhcp_header_sizes[i]] = '\0';
vp->length = strlen(vp->vp_strvalue);
break;
-
+
case PW_TYPE_OCTETS: /* only for Client HW Address */
memcpy(vp->vp_octets, p, packet->data[2]);
vp->length = packet->data[2];
break;
-
+
case PW_TYPE_ETHERNET: /* only for Client HW Address */
memcpy(vp->vp_ether, p, sizeof(vp->vp_ether));
vp->length = sizeof(vp->vp_ether);
break;
-
+
default:
fr_strerror_printf("Internal sanity check failed %d %d", vp->type, __LINE__);
pairfree(&vp);
break;
}
-
+
p += dhcp_header_sizes[i];
-
+
vp_prints(buffer, sizeof(buffer), vp);
fr_strerror_printf("\t%s", buffer);
pairfree(&vp);
}
/*
- * Jump over DHCP magic number, response, etc.
+ * Jump over DHCP magic number, response, etc.
*/
p = pp;
}
VALUE_PAIR **array, **last;
array = malloc(num_vps * sizeof(VALUE_PAIR *));
-
+
i = 0;
for (vp = packet->vps; vp != NULL; vp = vp->next) {
array[i++] = vp;
}
-
+
/*
* Sort the attributes.
*/
qsort(array, (size_t) num_vps, sizeof(VALUE_PAIR *),
attr_cmp);
-
+
last = &packet->vps;
for (i = 0; i < num_vps; i++) {
*last = array[i];
uint8_t *plength, *pattr;
if (!IS_DHCP_ATTR(vp)) goto next;
- if (vp->attribute == DHCP2ATTR(53)) goto next; /* already done */
+
+ /* DHCP-Message-Type (already done) */
+ if (vp->attribute == DHCP2ATTR(53)) goto next;
if (((vp->attribute & 0xffff) > 255) &&
(DHCP_BASE_ATTR(vp->attribute) != PW_DHCP_OPTION_82)) goto next;
fr_strerror_printf("WARNING Ignoring too long attribute %s!", vp->name);
break;
}
-
+
*plength += length;
p += length;
*/
packet->data_len = dhcp_size;
- if (original) {
- /*
- * FIXME: This may set it to broadcast, which we don't
- * want. Instead, set it to the real address of the
- * socket.
- */
- packet->src_ipaddr = original->dst_ipaddr;
-
- packet->sockfd = original->sockfd;
- }
-
if (packet->data_len < DEFAULT_PACKET_SIZE) {
memset(packet->data + packet->data_len, 0,
DEFAULT_PACKET_SIZE - packet->data_len);
return 0;
#else
+ fd = fd;
+ interface = interface;
+ macaddr = macaddr;
+ ip = ip;
+
fr_strerror_printf("Adding ARP entry is unsupported on this system");
return -1;
#endif
/*
- * dhcp.c DHCP processing. Done poorly for now.
+ * dhcp.c DHCP processing.
*
* Version: $Id$
*
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
* Copyright 2008 The FreeRADIUS server project
- * Copyright 2008 Alan DeKok <aland@deployingradius.com>
+ * Copyright 2008,2011 Alan DeKok <aland@deployingradius.com>
*/
#ifdef WITH_DHCP
/*
* DHCP-specific additions.
- */
+ */
int suppress_responses;
RADCLIENT dhcp_client;
} dhcp_socket_t;
request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
request->packet->dst_port = request->packet->dst_port;
- if (fr_dhcp_encode(request->packet, NULL) < 0) {
+ if (fr_dhcp_encode(request->packet) < 0) {
DEBUG("dhcprelay_process_client_request: ERROR in fr_dhcp_encode\n");
return -1;
}
}
}
- if (fr_dhcp_encode(request->packet, NULL) < 0) {
+ if (fr_dhcp_encode(request->packet) < 0) {
DEBUG("dhcprelay_process_server_reply: ERROR in fr_dhcp_encode\n");
return -1;
}
return fr_dhcp_send(request->packet);
}
+static const uint32_t attrnums[] = {
+ 57, /* DHCP-DHCP-Maximum-Msg-Size */
+ 256, /* DHCP-Opcode */
+ 257, /* DHCP-Hardware-Type */
+ 258, /* DHCP-Hardware-Address-Length */
+ 259, /* DHCP-Hop-Count */
+ 260, /* DHCP-Transaction-Id */
+ 262, /* DHCP-Flags */
+ 263, /* DHCP-Client-IP-Address */
+ 266, /* DHCP-Gateway-IP-Address */
+ 267 /* DHCP-Client-Hardware-Address */
+};
+
static int dhcp_process(REQUEST *request)
{
int rcode;
+ unsigned int i;
VALUE_PAIR *vp;
+ dhcp_socket_t *sock;
vp = pairfind(request->packet->vps, DHCP2ATTR(53)); /* DHCP-Message-Type */
if (vp) {
rcode = RLM_MODULE_FAIL;
}
- /*
- * For messages from a client, look for Relay attribute,
- * and forward it if necessary.
- */
- vp = NULL;
- if (request->packet->data[0] == 1) {
- vp = pairfind(request->config_items, DHCP2ATTR(270));
- }
- if (vp) {
- VALUE_PAIR *giaddr;
-
- /*
- * Find the original giaddr.
- * FIXME: Maybe look in the original packet?
- *
- * It's invalid to have giaddr=0 AND a relay option
- */
- 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;
- }
-
- /*
- * Forward a reply...
- */
- 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;
- /*
- * Don't change the destination port. It's the
- * server port.
- */
-
- /*
- * Hop count goes up.
- */
- vp = pairfind(request->reply->vps, DHCP2ATTR(259));
- if (vp) vp->vp_integer++;
-
- return 1;
- }
-
- /*
- * Responses from a server. Handle them differently.
- */
- 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;
-
- /*
- * Delete any existing giaddr. If we received a
- * message from the server, then we're NOT the
- * server. So we must be the destination of the
- * giaddr field.
- */
- pairdelete(&request->reply->vps, DHCP2ATTR(266));
-
- /*
- * Search for client IP address.
- */
- vp = pairfind(request->reply->vps, DHCP2ATTR(264));
- if (!vp) {
- request->reply->code = 0;
- RDEBUG("DHCP: No YIAddr in the reply. Discarding packet");
- return 1;
- }
-
- /*
- * FROM us, TO the client's IP, OUR port + 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;
-
- /*
- * Hop count goes down.
- */
- vp = pairfind(request->reply->vps, DHCP2ATTR(259));
- if (vp && (vp->vp_integer > 0)) vp->vp_integer--;
-
- /*
- * FIXME: Keep original somewhere? If the
- * broadcast flags are set, use them here?
- */
-
- return 1;
- }
-
vp = pairfind(request->reply->vps, DHCP2ATTR(53)); /* DHCP-Message-Type */
if (vp) {
request->reply->code = vp->vp_integer;
case RLM_MODULE_FAIL:
case RLM_MODULE_INVALID:
case RLM_MODULE_NOOP:
- case RLM_MODULE_NOTFOUND:
+ case RLM_MODULE_NOTFOUND:
if (request->packet->code == PW_DHCP_DISCOVER) {
request->reply->code = 0; /* ignore the packet */
} else {
break;
case RLM_MODULE_HANDLED:
+ request->reply->code = 0; /* ignore the packet */
break;
}
+ /*
+ * TODO: Handle 'output' of RLM_MODULE when acting as a
+ * DHCP relay We may want to not forward packets in
+ * certain circumstances.
+ */
+
+ /*
+ * Handle requests when acting as a DHCP relay
+ */
+ vp = pairfind(request->packet->vps, DHCP2ATTR(256)); /* DHCP-Opcode */
+ if (!vp) {
+ RDEBUG("FAILURE: Someone deleted the DHCP-Opcode!");
+ return 1;
+ }
+
+ /* BOOTREPLY received on port 67 (i.e. from a server) */
+ if (vp->vp_integer == 2) {
+ return dhcprelay_process_server_reply(request);
+ }
+
+ /* Packet from client, and we have DHCP-Relay-To-IP-Address */
+ if (pairfind(request->config_items, DHCP2ATTR(270))) {
+ return dhcprelay_process_client_request(request);
+ }
+
+ /* else it's a packet from a client, without relaying */
+ rad_assert(vp->vp_integer == 1); /* BOOTREQUEST */
+ sock = request->listener->data;
+
+ /*
+ * Handle requests when acting as a DHCP server
+ */
+
/*
* Releases don't get replies.
*/
request->reply->code = 0;
}
+ if (request->reply->code == 0) {
+ return 1;
+ }
+
+ request->reply->sockfd = request->packet->sockfd;
+
+ /*
+ * Copy specific fields from packet to reply, if they
+ * don't already exist
+ */
+ for (i = 0; i < sizeof(attrnums) / sizeof(attrnums[0]); i++) {
+ uint32_t attr = attrnums[i];
+
+ if (pairfind(request->reply->vps, DHCP2ATTR(attr))) continue;
+ if ((vp = pairfind(request->packet->vps, DHCP2ATTR(attr)))) {
+ pairadd(&request->reply->vps, paircopyvp(vp));
+ }
+ }
+
+ vp = pairfind(request->reply->vps, DHCP2ATTR(256)); /* DHCP-Opcode */
+ rad_assert(vp != NULL);
+ vp->vp_integer = 2; /* BOOTREPLY */
+
+ /*
+ * Prepare the reply packet for sending through dhcp_socket_send()
+ */
+ request->reply->dst_ipaddr.af = AF_INET;
+ request->reply->src_ipaddr.af = AF_INET;
+ /* XXX sock->ipaddr == 0 (listening on '*') */
+ request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = sock->ipaddr.ipaddr.ip4addr.s_addr;
+
+ request->reply->dst_port = request->packet->src_port;
+ request->reply->src_port = request->packet->dst_port;
+
+ vp = pairfind(request->reply->vps, DHCP2ATTR(266)); /* DHCP-Gateway-IP-Address */
+ if (vp && (vp->vp_ipaddr != htonl(INADDR_ANY))) {
+ /* Answer to client's nearest DHCP relay */
+ request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
+ } else if ((request->reply->code == PW_DHCP_NAK) ||
+ ((vp = pairfind(request->reply->vps, DHCP2ATTR(262))) /* DHCP-Flags */ &&
+ (vp->vp_integer & 0x8000) &&
+ ((vp = pairfind(request->reply->vps, DHCP2ATTR(263))) /* DHCP-Client-IP-Address */ &&
+ (vp->vp_ipaddr == htonl(INADDR_ANY))))) {
+ /*
+ * RFC 2131, page 23
+ *
+ * Broadcast on
+ * - DHCPNAK
+ * or
+ * - Broadcast flag is set up and ciaddr == NULL
+ */
+ request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST);
+ } else {
+ /*
+ * RFC 2131, page 23
+ *
+ * Unicast to
+ * - ciaddr if present
+ * otherwise to yiaddr
+ */
+ if ((vp = pairfind(request->reply->vps, DHCP2ATTR(263))) /* DHCP-Client-IP-Address */ &&
+ (vp->vp_ipaddr != htonl(INADDR_ANY))) {
+ request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
+ } else {
+ vp = pairfind(request->reply->vps, DHCP2ATTR(264)); /* DHCP-Your-IP-Address */
+ rad_assert(vp != NULL);
+ request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
+
+ /*
+ * 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->reply->code == PW_DHCP_OFFER) {
+ VALUE_PAIR *hwvp = pairfind(request->reply->vps, DHCP2ATTR(267)); /* DHCP-Client-Hardware-Address */
+ rad_assert(hwvp != NULL);
+ if (fr_dhcp_add_arp_entry(request->reply->sockfd, sock->interface, hwvp, vp) < 0) {
+ return -1;
+ }
+ }
+ }
+ }
+
return 1;
}
if (request->reply->code == 0) return 0; /* don't reply */
- 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;
- }
+ if (fr_dhcp_encode(request->reply) < 0) {
+ DEBUG("dhcp_socket_send: ERROR\n");
+ return -1;
}
-
sock = listener->data;
if (sock->suppress_responses) return 0;
}
-static int dhcp_socket_encode(UNUSED rad_listen_t *listener, REQUEST *request)
+static int dhcp_socket_encode(UNUSED rad_listen_t *listener, UNUSED REQUEST *request)
{
DEBUG2("NO ENCODE!");
return 0;
- return fr_dhcp_encode(request->reply, request->packet);
}