*
* Version: $Id$
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
* Copyright 2008 The FreeRADIUS server project
#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)
if (packet->data_len < MIN_PACKET_SIZE) {
fr_strerror_printf("DHCP packet is too small (%d < %d)",
- packet->data_len, MIN_PACKET_SIZE);
+ (int) packet->data_len, MIN_PACKET_SIZE);
rad_free(&packet);
return NULL;
}
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!
+ */
+ }
+
#ifndef WITH_UDPFROMTO
/*
* Assume that the packet is encoded before sending it.
p = data;
while (p < (data + data_len)) {
- vp = paircreate(tlv->attribute | (p[0] << 8), PW_TYPE_OCTETS);
+ vp = paircreate(tlv->attribute | (p[0] << 8), DHCP_MAGIC_VENDOR, PW_TYPE_OCTETS);
if (!vp) {
pairfree(&head);
goto make_tlv;
* The caller allocated TLV, so we need to copy the FIRST
* attribute over top of that.
*/
- memcpy(tlv, head, sizeof(*tlv));
- head->next = NULL;
- pairfree(&head);
+ if (head) {
+ memcpy(tlv, head, sizeof(*tlv));
+ head->next = NULL;
+ pairfree(&head);
+ }
+
return 0;
make_tlv:
continue;
}
- da = dict_attrbyvalue(DHCP2ATTR(p[0]));
+ da = dict_attrbyvalue(p[0], DHCP_MAGIC_VENDOR);
if (!da) {
fr_strerror_printf("Attribute not in our dictionary: %u",
p[0]);
/*
* Hack for ease of use.
*/
- if ((da->attr == DHCP2ATTR(0x3d)) &&
+ if ((da->attr == 0x3d) &&
!da->flags.array &&
(alen == 7) && (*p == 1) && (num_entries == 1)) {
vp->type = PW_TYPE_ETHERNET;
return -1;
}
- debug_pair(vp);
*tail = vp;
- while (*tail) tail = &(*tail)->next;
+ while (*tail) {
+ debug_pair(*tail);
+ tail = &(*tail)->next;
+ }
p += alen;
} /* loop over array entries */
} /* loop over the entire packet */
/*
* DHCP Opcode is request
*/
- vp = pairfind(head, DHCP2ATTR(256));
- if (vp && vp->lvalue == 3) {
+ vp = pairfind(head, 256, DHCP_MAGIC_VENDOR);
+ if (vp && vp->vp_integer == 3) {
/*
* Vendor is "MSFT 98"
*/
- vp = pairfind(head, DHCP2ATTR(63));
+ vp = pairfind(head, 63, DHCP_MAGIC_VENDOR);
if (vp && (strcmp(vp->vp_strvalue, "MSFT 98") == 0)) {
- vp = pairfind(head, DHCP2ATTR(262));
+ vp = pairfind(head, 262, DHCP_MAGIC_VENDOR);
/*
* Reply should be broadcast.
*/
- if (vp) vp->lvalue |= 0x8000;
+ if (vp) vp->vp_integer |= 0x8000;
packet->data[10] |= 0x80;
}
}
* Client can request a LARGER size, but not a smaller
* one. They also cannot request a size larger than MTU.
*/
- maxms = pairfind(packet->vps, DHCP2ATTR(57));
- mtu = pairfind(packet->vps, DHCP2ATTR(26));
+ maxms = pairfind(packet->vps, 57, DHCP_MAGIC_VENDOR);
+ mtu = pairfind(packet->vps, 26, DHCP_MAGIC_VENDOR);
if (mtu && (mtu->vp_integer < DEFAULT_PACKET_SIZE)) {
fr_strerror_printf("DHCP Fatal: Client says MTU is smaller than minimum permitted by the specification.");
/*
* DHCP-Message-Type is first, for simplicity.
*/
- if (((*a)->attribute == DHCP2ATTR(53)) &&
- (*b)->attribute != DHCP2ATTR(53)) return -1;
+ if (((*a)->attribute == 53) &&
+ (*b)->attribute != 53) return -1;
/*
* Relay-Agent is last
*/
- if (((*a)->attribute == DHCP2ATTR(82)) &&
- (*b)->attribute != DHCP2ATTR(82)) return +1;
+ if (((*a)->attribute == 82) &&
+ (*b)->attribute != 82) return +1;
return ((*a)->attribute - (*b)->attribute);
}
attribute = vps->attribute & 0xffff00ff;
- tlv = paircreate(attribute, PW_TYPE_TLV);
+ tlv = paircreate(attribute, DHCP_MAGIC_VENDOR, PW_TYPE_TLV);
if (!tlv) return NULL;
tlv->length = 0;
* non-TLV attribute.
*/
if (!vp->flags.is_tlv ||
- vp->flags.encoded ||
+ vp->flags.extended ||
((vp->attribute & 0xffff00ff) != attribute)) {
break;
}
ptr = tlv->vp_tlv;
for (vp = vps; vp != NULL; vp = vp->next) {
if (!vp->flags.is_tlv ||
- vp->flags.encoded ||
+ vp->flags.extended ||
((vp->attribute & 0xffff00ff) != attribute)) {
break;
}
ptr[1] = length;
ptr += length + 2;
- vp->flags.encoded = 1;
+ vp->flags.extended = 1;
}
return tlv;
* to the client.
*
* if giaddr, send to giaddr.
- * if NAK, send broadcast packet
- * if ciaddr, unicast to ciaddr
- * if flags & 0x8000, broadcast (client request)
- * if sent from 0.0.0.0, broadcast response
- * unicast to client yiaddr
+ * if NAK, send broadcast.
+ * if broadcast flag, send broadcast.
+ * if ciaddr is empty, send broadcast.
+ * otherwise unicast to ciaddr.
*/
/*
*/
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;
packet->dst_port = original->src_port; /* debugging */
}
- } else if (packet->code == PW_DHCP_NAK) {
- packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST);
-
- } else if (dhcp->ciaddr != htonl(INADDR_ANY)) {
- packet->dst_ipaddr.ipaddr.ip4addr.s_addr = dhcp->ciaddr;
-
- } else if ((dhcp->flags & 0x8000) != 0) {
- packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST);
-
- } else if (packet->dst_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_ANY)) {
+ } 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 if (dhcp->yiaddr != htonl(INADDR_ANY)) {
- packet->dst_ipaddr.ipaddr.ip4addr.s_addr = dhcp->yiaddr;
-
} else {
- /* leave destination IP alone. */
+ /*
+ * 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;
}
/*
&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, i,
+ DHCP_MAGIC_VENDOR);
+ if (!vp) continue;
+
+ debug_pair(vp);
+ }
+ }
}
p = packet->data;
* smaller one. They also cannot request a size
* larger than MTU.
*/
- vp = pairfind(original->vps, DHCP2ATTR(57));
+ vp = pairfind(original->vps, 57, DHCP_MAGIC_VENDOR);
if (vp && (vp->vp_integer > mms)) {
mms = vp->vp_integer;
/*
* RFC 3118: Authentication option.
*/
- vp = pairfind(packet->vps, DHCP2ATTR(90));
+ vp = pairfind(packet->vps, 90, DHCP_MAGIC_VENDOR);
if (vp) {
if (vp->length < 2) {
memset(vp->vp_octets + vp->length, 0,
VALUE_PAIR *pass;
vp->vp_octets[1] = 0;
- pass = pairfind(packet->vps, PW_CLEARTEXT_PASSWORD);
+ pass = pairfind(packet->vps, PW_CLEARTEXT_PASSWORD, DHCP_MAGIC_VENDOR);
if (pass) {
length = pass->length;
if ((length + 11) > sizeof(vp->vp_octets)) {
}
}
- if (!original) {
- *p++ = 1; /* client message */
+ vp = pairfind(packet->vps, 256, DHCP_MAGIC_VENDOR);
+ if (vp) {
+ *p++ = vp->vp_integer & 0xff;
} else {
- *p++ = 2; /* server message */
+ if (!original) {
+ *p++ = 1; /* client message */
+ } else {
+ *p++ = 2; /* server message */
+ }
}
*p++ = 1; /* hardware type = ethernet */
*p++ = 6; /* 6 bytes of ethernet */
- *p++ = 0; /* hops */
+
+ vp = pairfind(packet->vps, 259, DHCP_MAGIC_VENDOR);
+ if (vp) {
+ *p++ = vp->vp_integer & 0xff;
+ } else {
+ *p++ = 0; /* hops */
+ }
if (original) { /* Xid */
memcpy(p, original->data + 4, 4);
/*
* Allow the admin to set the broadcast flag.
*/
- vp = pairfind(packet->vps, DHCP2ATTR(262));
+ vp = pairfind(packet->vps, 262, DHCP_MAGIC_VENDOR);
if (vp) {
p[0] |= (vp->vp_integer & 0xff00) >> 8;
p[1] |= (vp->vp_integer & 0xff);
/*
* Set client IP address.
*/
- vp = pairfind(packet->vps, DHCP2ATTR(264)); /* Your IP address */
+ vp = pairfind(packet->vps, 264, DHCP_MAGIC_VENDOR); /* Your IP address */
if (vp) {
lvalue = vp->vp_ipaddr;
} else {
memcpy(p, &lvalue, 4); /* your IP address */
p += 4;
- vp = pairfind(packet->vps, DHCP2ATTR(265)); /* server IP address */
- if (!vp) vp = pairfind(packet->vps, DHCP2ATTR(54)); /* identifier */
+ vp = pairfind(packet->vps, 265, DHCP_MAGIC_VENDOR); /* server IP address */
+ if (!vp) vp = pairfind(packet->vps, 54, DHCP_MAGIC_VENDOR); /* identifier */
if (vp) {
lvalue = vp->vp_ipaddr;
} else {
if (original) {
memcpy(p, original->data + 24, 4); /* copy gateway IP address */
} else {
- vp = pairfind(packet->vps, DHCP2ATTR(266));
+ vp = pairfind(packet->vps, 266, DHCP_MAGIC_VENDOR);
if (vp) {
lvalue = vp->vp_ipaddr;
} else {
if (original) {
memcpy(p, original->data + 28, DHCP_CHADDR_LEN);
} else {
- vp = pairfind(packet->vps, DHCP2ATTR(267));
+ vp = pairfind(packet->vps, 267, DHCP_MAGIC_VENDOR);
if (vp) {
if (vp->length > DHCP_CHADDR_LEN) {
memcpy(p, vp->vp_octets, DHCP_CHADDR_LEN);
}
p += DHCP_CHADDR_LEN;
- memset(p, 0, 192); /* bootp legacy */
- p += 192;
+ /*
+ * Zero our sname && filename fields.
+ */
+ memset(p, 0, DHCP_SNAME_LEN + DHCP_FILE_LEN);
+ p += DHCP_SNAME_LEN;
+
+ /*
+ * Copy over DHCP-Boot-Filename.
+ *
+ * FIXME: This copy should be delayed until AFTER the options
+ * have been processed. If there are too many options for
+ * the packet, then they go into the sname && filename fields.
+ * When that happens, the boot filename is passed as an option,
+ * instead of being placed verbatim in the filename field.
+ */
+ vp = pairfind(packet->vps, 269, DHCP_MAGIC_VENDOR);
+ if (vp) {
+ if (vp->length > DHCP_FILE_LEN) {
+ memcpy(p, vp->vp_strvalue, DHCP_FILE_LEN);
+ } else {
+ memcpy(p, vp->vp_strvalue, vp->length);
+ }
+ }
+ p += DHCP_FILE_LEN;
lvalue = htonl(DHCP_OPTION_MAGIC_NUMBER); /* DHCP magic number */
memcpy(p, &lvalue, 4);
vp = packet->vps;
while (vp) {
int num_entries = 1;
-
VALUE_PAIR *same;
uint8_t *plength, *pattr;
- if (!IS_DHCP_ATTR(vp)) goto next;
- if (vp->attribute == DHCP2ATTR(53)) goto next; /* already done */
- if (((vp->attribute & 0xffff) > 255) &&
+ if (vp->vendor != DHCP_MAGIC_VENDOR) goto next;
+ if (vp->attribute == 53) goto next; /* already done */
+ if ((vp->attribute > 255) &&
(DHCP_BASE_ATTR(vp->attribute) != PW_DHCP_OPTION_82)) goto next;
debug_pair(vp);
+ if (vp->flags.extended) goto next;
+
length = vp->length;
for (same = vp->next; same != NULL; same = same->next) {
}
if (vp->flags.is_tlv) {
- VALUE_PAIR *tlv = fr_dhcp_vp2suboption(vp);
- if (vp) {
- tlv->next = vp->next;
- vp->next = tlv;
- }
-
+ VALUE_PAIR *tlv;
+
/*
- * The encoded flag MUST be set in the vp!
+ * Should NOT have been encoded yet!
*/
- vp = vp->next;
+ tlv = fr_dhcp_vp2suboption(vp);
+
+ /*
+ * Ignore it if there's an issue
+ * encoding it.
+ */
+ if (!tlv) goto next;
+
+ tlv->next = vp->next;
+ vp->next = tlv;
+ vp = tlv;
}
length = fr_dhcp_vp2attr(vp, p, 0);
return 0;
}
+
+int fr_dhcp_add_arp_entry(int fd, const char *interface,
+ VALUE_PAIR *macaddr, VALUE_PAIR *ip)
+{
+#ifdef SIOCSARP
+ struct sockaddr_in *sin
+ struct arpreq req;
+
+ if (macaddr->length > sizeof (req.arp_ha.sa_data)) {
+ fr_strerror_printf("ERROR: DHCP only supports up to %d octets for "
+ "Client Hardware Address (got %d octets)\n",
+ sizeof(req.arp_ha.sa_data),
+ macaddr->length);
+ return -1;
+ }
+
+ memset(&req, 0, sizeof(req));
+ sin = (struct sockaddr_in *) &req.arp_pa;
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = ip->vp_ipaddr;
+ strlcpy(req.arp_dev, interface, sizeof(req.arp_dev));
+ memcpy(&req.arp_ha.sa_data, macaddr->vp_octets, macaddr->length);
+
+ req.arp_flags = ATF_COM;
+ if (ioctl(fd, SIOCSARP, &req) < 0) {
+ fr_strerror_printf("DHCP: Failed to add entry in ARP cache: %s (%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ return 0;
+#else
+ fd = fd; /* -Wunused */
+ interface = interface; /* -Wunused */
+ macaddr = macaddr; /* -Wunused */
+ ip = ip; /* -Wunused */
+
+ fr_strerror_printf("Adding ARP entry is unsupported on this system");
+ return -1;
+#endif
+}
+
#endif /* WITH_DHCP */