cucidhcp merged
Stephen R. van den Berg
srb at cuci.nl
Mon Aug 22 21:37:54 CEST 2011
---
src/lib/dhcp.c | 431 ++++++++++++++++++++++-------------------------------
src/main/dhcpd.c | 227 +++++++++++------------------
src/main/listen.c | 4 +-
3 files changed, 262 insertions(+), 400 deletions(-)
diff --git a/src/lib/dhcp.c b/src/lib/dhcp.c
index 971fac7..a59edf3 100644
--- a/src/lib/dhcp.c
+++ b/src/lib/dhcp.c
@@ -1,8 +1,6 @@
/*
* dhcp.c Functions to send/receive dhcp packets.
*
- * Version: $Id$
- *
* 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
@@ -28,41 +26,69 @@ 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)
-#define DHCP_VEND_LEN (308)
-#define DHCP_OPTION_MAGIC_NUMBER (0x63825363)
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <netinet/udp.h>
+#include <netinet/ip.h>
+
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
#ifndef INADDR_BROADCAST
#define INADDR_BROADCAST INADDR_NONE
#endif
+/* DHCP protocol -- see RFC 2131 */
+#define DHCP_OPTION_MAGIC_NUMBER 0x63825363
+
+#define DHCP_PADDING 0x00
+
+#define DHCP_END 0xFF
+
+#define BOOTREQUEST 1
+#define BOOTREPLY 2
+
+#define ETH_10MB 1
+
+#define BROADCAST_FLAG 0x8000
+
+#define OPTION_FIELD 0
+#define FILE_FIELD 1
+#define SNAME_FIELD 2
+
+#define MAC_BCAST_ADDR "\xff\xff\xff\xff\xff\xff"
+
typedef struct dhcp_packet_t {
uint8_t opcode;
uint8_t htype;
uint8_t hlen;
uint8_t hops;
- uint32_t xid; /* 4 */
- uint16_t secs; /* 8 */
+ uint32_t xid;
+ uint16_t secs;
uint16_t flags;
- uint32_t ciaddr; /* 12 */
- uint32_t yiaddr; /* 16 */
- uint32_t siaddr; /* 20 */
- uint32_t giaddr; /* 24 */
- uint8_t chaddr[DHCP_CHADDR_LEN]; /* 28 */
- uint8_t sname[DHCP_SNAME_LEN]; /* 44 */
- uint8_t file[DHCP_FILE_LEN]; /* 108 */
- uint32_t option_format; /* 236 */
- uint8_t options[DHCP_VEND_LEN];
+ uint32_t ciaddr;
+ uint32_t yiaddr;
+ uint32_t siaddr;
+ uint32_t giaddr;
+ uint8_t chaddr[16];
+ uint8_t sname[64];
+ uint8_t file[128];
+ uint32_t cookie;
+ uint8_t options[308]; /* 312 - cookie */
+#if 0
+ uint8_t optionspad[924]; /* 1500 - options */
+#endif
} dhcp_packet_t;
+struct udp_dhcp_packet {
+ struct iphdr ip;
+ struct udphdr udp;
+ dhcp_packet_t data;
+};
+
typedef struct dhcp_option_t {
uint8_t code;
uint8_t length;
@@ -110,9 +136,7 @@ static int dhcp_header_sizes[] = {
1, 1, 1, 1,
4, 2, 2, 4,
4, 4, 4,
- DHCP_CHADDR_LEN,
- DHCP_SNAME_LEN,
- DHCP_FILE_LEN
+ 16, 64, 128
};
@@ -226,15 +250,8 @@ RADIUS_PACKET *fr_dhcp_recv(int sockfd)
packet->sockfd = sockfd;
sizeof_src = sizeof(src);
-#ifdef WITH_UDPFROMTO
- sizeof_dst = sizeof(dst);
- packet->data_len = recvfromto(sockfd, packet->data, MAX_PACKET_SIZE, 0,
- (struct sockaddr *)&src, &sizeof_src,
- (struct sockaddr *)&dst, &sizeof_dst);
-#else
packet->data_len = recvfrom(sockfd, packet->data, MAX_PACKET_SIZE, 0,
(struct sockaddr *)&src, &sizeof_src);
-#endif
if (packet->data_len <= 0) {
fr_strerror_printf("Failed reading DHCP socket: %s", strerror(errno));
@@ -323,12 +340,10 @@ RADIUS_PACKET *fr_dhcp_recv(int sockfd)
sizeof_dst = sizeof(dst);
-#ifndef WITH_UDPFROMTO
/*
* This should never fail...
*/
getsockname(sockfd, (struct sockaddr *) &dst, &sizeof_dst);
-#endif
fr_sockaddr2ipaddr(&dst, sizeof_dst, &packet->dst_ipaddr, &port);
packet->dst_port = port;
@@ -364,48 +379,77 @@ RADIUS_PACKET *fr_dhcp_recv(int sockfd)
return packet;
}
+static int kernelpacket(const dhcp_packet_t*msg,u_int32_t giaddr,unsigned port,
+ int sockfd)
+{ struct sockaddr_in dst;
+ dst.sin_family=AF_INET;dst.sin_port=htons(port);dst.sin_addr.s_addr=giaddr;
+ return sendto(sockfd,msg,sizeof*msg,0,(struct sockaddr*)&dst,sizeof(dst));
+}
+
+static u_int16_t checksum(const void*addr,unsigned pcount)
+{ unsigned long sum;const u_int16_t*src;int count;
+ for(count=pcount,sum=0,src=addr;count>1;count-=2)
+ sum+=*src++;
+ if(count>0)
+ { u_int16_t tmp=0;
+ *(unsigned char*)&tmp=*(const unsigned char*)src;
+ sum+=tmp;
+ }
+ sum=(sum&0xffff)+(sum>>16);
+ sum=(sum&0xffff)+(sum>>16); /* carry */
+ return ~sum;
+}
+
+static int rawpacket(const dhcp_packet_t*msg,unsigned port,
+ u_int32_t ciaddr,unsigned cport,const unsigned char*chaddr,
+ const RADIUS_PACKET*rp)
+{ struct sockaddr_ll dst;
+ struct udp_dhcp_packet pkt;
+ memset(&dst,0,sizeof dst);memset(&pkt,0,sizeof pkt);
+ dst.sll_family=AF_PACKET;memcpy(dst.sll_addr,chaddr,dst.sll_halen=6);
+ dst.sll_protocol=htons(ETH_P_IP);dst.sll_ifindex=rp->hash;
+ pkt.ip.protocol=IPPROTO_UDP;
+ pkt.ip.saddr=rp->src_ipaddr.ipaddr.ip4addr.s_addr;pkt.ip.daddr=ciaddr;
+ pkt.udp.source=htons(port);pkt.udp.dest=htons(cport);
+ pkt.udp.len=htons(sizeof pkt.udp+sizeof*msg);
+ pkt.ip.tot_len=pkt.udp.len; /* cheat on the pseudo-header */
+ memcpy(&pkt.data,msg,sizeof*msg);pkt.udp.check=checksum(&pkt,sizeof pkt);
+ pkt.ip.tot_len=htons(sizeof pkt);pkt.ip.ihl=sizeof(pkt.ip)>>2;
+ pkt.ip.version=IPVERSION;pkt.ip.ttl=IPDEFTTL;
+ pkt.ip.check=checksum(&pkt.ip,sizeof pkt.ip);
+ return sendto(rp->id,&pkt,sizeof pkt,0,(struct sockaddr*)&dst,
+ sizeof dst);
+}
/*
* Send a DHCP packet.
*/
int fr_dhcp_send(RADIUS_PACKET *packet)
{
- struct sockaddr_storage dst;
- socklen_t sizeof_dst;
-#ifdef WITH_UDPFROMTO
- struct sockaddr_storage src;
- socklen_t sizeof_src;
-#endif
+ const dhcp_packet_t*nm = (dhcp_packet_t*)packet->data;
+
+ if(nm->giaddr)
+ return kernelpacket(nm,nm->giaddr,packet->src_port,packet->sockfd);
+ else
+ { const unsigned char*chaddr;in_addr_t ciaddr;
+ static const unsigned char mbcast[]=MAC_BCAST_ADDR;
+ ciaddr=INADDR_BROADCAST;
+ if(packet->code==PW_DHCP_NAK)
+ chaddr=mbcast;
+ else
+ { chaddr=nm->chaddr;
+ if(packet->code!=PW_DHCP_OFFER
+ &&(nm->ciaddr||!(nm->flags&htons(BROADCAST_FLAG))))
+ ciaddr=nm->yiaddr;
+ }
+ if(ciaddr==nm->ciaddr||ciaddr==INADDR_BROADCAST&&chaddr==mbcast
+ ||packet->id<0)
+ return kernelpacket(nm,ciaddr,packet->dst_port,packet->sockfd);
+ else
+ return rawpacket(nm,packet->src_port,ciaddr,packet->dst_port,
+ chaddr,packet);
+ }
- 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.
- */
- return sendto(packet->sockfd, packet->data, packet->data_len, 0,
- (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);
-#endif
}
static int fr_dhcp_attr2vp(VALUE_PAIR *vp, const uint8_t *p, size_t alen);
@@ -960,17 +1004,17 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
int i, num_vps;
uint8_t *p;
VALUE_PAIR *vp;
- uint32_t lvalue, mms;
+ uint32_t mms;
size_t dhcp_size, length;
- dhcp_packet_t *dhcp;
+ dhcp_packet_t *nm,*dhcp;
char buffer[1024];
if (packet->data) return 0;
- packet->data = malloc(MAX_PACKET_SIZE);
- if (!packet->data) return -1;
+ packet->data = (void*)(nm = malloc(MAX_PACKET_SIZE));
+ if (!nm) return -1;
- packet->data_len = MAX_PACKET_SIZE;
+ memset(nm, 0, packet->data_len = MAX_PACKET_SIZE);
if (packet->code == 0) packet->code = PW_DHCP_NAK;
@@ -1005,59 +1049,18 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
* 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)) {
- /*
- * The kernel will take care of sending it to
- * the broadcast MAC.
- */
- packet->dst_ipaddr.ipaddr.ip4addr.s_addr = lvalue;
- } else if (dhcp->ciaddr == htonl(INADDR_ANY))) {
+ /* common defaults */
- vp = pairfind(packet->vps, 264, DHCP_MAGIC_VENDOR); /* Your IP address */
- if (vp) {
- lvalue = vp->vp_ipaddr;
- packet->offset = 0;
- } else {
- lvalue = htonl(INADDR_BROADCAST);
- }
- packet->dst_ipaddr.ipaddr.ip4addr.s_addr = lvalue;
- } else {
- /*
- * It was unicast to us: we need to
- * unicast the response.
- */
- if (packet->src_ipaddr.ipaddr.ip4addr.s_addr != dhcp->ciaddr) {
- packet->offset = 0;
- }
- packet->dst_ipaddr.ipaddr.ip4addr.s_addr = dhcp->ciaddr;
- }
+ nm->xid = dhcp->xid;
- /*
- * 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);
- }
+ memcpy(&nm->chaddr, &dhcp->chaddr, sizeof nm->chaddr);
+ nm->flags = dhcp->flags;
+ nm->giaddr = dhcp->giaddr;
+ nm->ciaddr = dhcp->ciaddr;
+
+ } else
+ nm->xid = fr_rand();
if (fr_debug_flag > 1) {
char type_buf[64];
@@ -1094,8 +1097,6 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
}
}
- p = packet->data;
-
mms = DEFAULT_PACKET_SIZE; /* maximum message size */
if (original) {
@@ -1159,137 +1160,57 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
}
}
- vp = pairfind(packet->vps, 256, DHCP_MAGIC_VENDOR);
- if (vp) {
- *p++ = vp->vp_integer & 0xff;
- } else {
- if (!original) {
- *p++ = 1; /* client message */
- } else {
- *p++ = 2; /* server message */
- }
- }
- *p++ = 1; /* hardware type = ethernet */
- *p++ = 6; /* 6 bytes of ethernet */
+ nm->opcode = BOOTREPLY;
- vp = pairfind(packet->vps, 259, DHCP_MAGIC_VENDOR);
- if (vp) {
- *p++ = vp->vp_integer & 0xff;
- } else {
- *p++ = 0; /* hops */
- }
+ if (vp = pairfind(packet->vps, 256, DHCP_MAGIC_VENDOR))
+ nm->opcode = vp->vp_integer & 0xff;
+ else if (!original)
+ nm->opcode = BOOTREQUEST; /* client message */
- if (original) { /* Xid */
- memcpy(p, original->data + 4, 4);
- } else {
- lvalue = fr_rand();
- memcpy(p, &lvalue, 4);
- }
- p += 4;
-
- memset(p, 0, 2); /* secs are zero */
- p += 2;
-
- if (original) {
- memcpy(p, original->data + 10, 6); /* copy flags && ciaddr */
- }
+ nm->htype = ETH_10MB;
+ nm->hlen = IFHWADDRLEN;
+ nm->cookie = htonl(DHCP_OPTION_MAGIC_NUMBER);
+ nm->options[0] = DHCP_END;
+ if (vp = pairfind(packet->vps, 259, DHCP_MAGIC_VENDOR))
+ nm->hops = vp->vp_integer & 0xff;
/*
* Allow the admin to set the broadcast flag.
*/
- vp = pairfind(packet->vps, 262, DHCP_MAGIC_VENDOR);
- if (vp) {
- p[0] |= (vp->vp_integer & 0xff00) >> 8;
- p[1] |= (vp->vp_integer & 0xff);
- }
-
- p += 6;
-
+ if (vp = pairfind(packet->vps, 262, DHCP_MAGIC_VENDOR))
+ nm->flags |= vp->vp_integer; /* would override be better? */
/*
* Set client IP address.
*/
vp = pairfind(packet->vps, 264, DHCP_MAGIC_VENDOR); /* Your IP address */
if (vp) {
- lvalue = vp->vp_ipaddr;
- } else {
- lvalue = htonl(INADDR_ANY);
- }
- memcpy(p, &lvalue, 4); /* your IP address */
- p += 4;
+ nm->yiaddr = vp->vp_ipaddr;
+ /* perform arpping FIXME */
+ } else
+ nm->yiaddr = htonl(INADDR_ANY);
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 {
- lvalue = htonl(INADDR_ANY);
- }
- memcpy(p, &lvalue, 4); /* Server IP address */
- p += 4;
+ /* server identifier */
+ if (!vp) vp = pairfind(packet->vps, 54, DHCP_MAGIC_VENDOR);
+ nm->siaddr = vp ? vp->vp_ipaddr : htonl(INADDR_ANY);
- if (original) {
- memcpy(p, original->data + 24, 4); /* copy gateway IP address */
- } else {
+ if (!original) {
vp = pairfind(packet->vps, 266, DHCP_MAGIC_VENDOR);
- if (vp) {
- lvalue = vp->vp_ipaddr;
- } else {
- lvalue = htonl(INADDR_NONE);
- }
- memcpy(p, &lvalue, 4);
- }
- p += 4;
+ nm->giaddr = vp ? vp->vp_ipaddr : htonl(INADDR_NONE);
- if (original) {
- memcpy(p, original->data + 28, DHCP_CHADDR_LEN);
- } else {
- vp = pairfind(packet->vps, 267, DHCP_MAGIC_VENDOR);
- if (vp) {
- if (vp->length > DHCP_CHADDR_LEN) {
- memcpy(p, vp->vp_octets, DHCP_CHADDR_LEN);
- } else {
- memcpy(p, vp->vp_octets, vp->length);
- }
+ if (vp = pairfind(packet->vps, 267, DHCP_MAGIC_VENDOR)) {
+ unsigned len = vp->length;
+ if (len > sizeof nm->chaddr)
+ len = sizeof nm->chaddr;
+ memcpy(nm->chaddr, vp->vp_octets, len);
}
}
- p += DHCP_CHADDR_LEN;
-
- /*
- * 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);
- p += 4;
/*
* Print the header.
*/
if (fr_debug_flag > 1) {
- uint8_t *pp = p;
-
- p = packet->data;
+ p = (unsigned char*)nm;
for (i = 0; i < 14; i++) {
vp = pairmake(dhcp_header_names[i], NULL, T_OP_EQ);
@@ -1328,8 +1249,8 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
break;
case PW_TYPE_OCTETS: /* only for Client HW Address */
- memcpy(vp->vp_octets, p, packet->data[2]);
- vp->length = packet->data[2];
+ memcpy(vp->vp_octets, p, nm->hlen);
+ vp->length = nm->hlen;
break;
case PW_TYPE_ETHERNET: /* only for Client HW Address */
@@ -1349,11 +1270,6 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
fr_strerror_printf("\t%s", buffer);
pairfree(&vp);
}
-
- /*
- * Jump over DHCP magic number, response, etc.
- */
- p = pp;
}
/*
@@ -1390,6 +1306,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
free(array);
}
+ p = nm->options;
p[0] = 0x35; /* DHCP-Message-Type */
p[1] = 1;
p[2] = packet->code - PW_DHCP_OFFSET;
@@ -1402,7 +1319,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
while (vp) {
int num_entries = 1;
VALUE_PAIR *same;
- uint8_t *plength, *pattr;
+ uint8_t *plength;
if (vp->vendor != DHCP_MAGIC_VENDOR) goto next;
if (vp->attribute == 53) goto next; /* already done */
@@ -1430,7 +1347,6 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
vp->vp_octets[0] = 1;
}
- pattr = p;
*(p++) = vp->attribute & 0xff;
plength = p;
*(p++) = 0; /* header isn't included in attr length */
@@ -1497,7 +1413,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
p[0] = 0xff; /* end of option option */
p[1] = 0x00;
p += 2;
- dhcp_size = p - packet->data;
+ dhcp_size = p - (unsigned char*)nm;
/*
* FIXME: if (dhcp_size > mms),
@@ -1514,19 +1430,24 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
*/
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 (vp = pairfind(packet->vps, 268, DHCP_MAGIC_VENDOR)) {
+ unsigned len = vp->length;
+ if (len > sizeof nm->sname)
+ len = sizeof nm->sname;
+ memcpy(nm->sname, vp->vp_strvalue, len);
+ }
+ /*
+ * Copy over DHCP-Boot-Filename.
+ */
+ if (vp = pairfind(packet->vps, 269, DHCP_MAGIC_VENDOR)) {
+ unsigned len = vp->length;
+ if (len > sizeof nm->file)
+ len = sizeof nm->file;
+ memcpy(nm->file, vp->vp_strvalue, len);
}
if (packet->data_len < DEFAULT_PACKET_SIZE) {
- memset(packet->data + packet->data_len, 0,
+ memset((char*)nm + packet->data_len, 0,
DEFAULT_PACKET_SIZE - packet->data_len);
packet->data_len = DEFAULT_PACKET_SIZE;
}
diff --git a/src/main/dhcpd.c b/src/main/dhcpd.c
index 7247e42..1d3e63d 100644
--- a/src/main/dhcpd.c
+++ b/src/main/dhcpd.c
@@ -1,7 +1,5 @@
/*
- * dhcp.c DHCP processing. Done poorly for now.
- *
- * Version: $Id$
+ * dhcp.c DHCP processing.
*
* 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
@@ -23,23 +21,31 @@
#ifdef WITH_DHCP
-/*
- * Same contents as listen_socket_t.
- */
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+
typedef struct dhcp_socket_t {
/*
- * For normal sockets.
+ * For normal sockets.
*/
- fr_ipaddr_t ipaddr;
- int port;
- const char *interface;
- RADCLIENT_LIST *clients;
+ fr_ipaddr_t ipaddr;
+ int port;
+ const char *interface;
+ RADCLIENT_LIST *clients;
/*
- * DHCP-specific additions.
- */
- int suppress_responses;
- RADCLIENT dhcp_client;
+ * DHCP-specific additions.
+ */
+ int suppress_responses;
+ RADCLIENT dhcp_client;
+ int rsocket;
+ unsigned ifindex;
+ unsigned char hwaddr[IFHWADDRLEN];
+ sa_family_t hwfamily;
+ fr_ipaddr_t netmask;
} dhcp_socket_t;
static int dhcp_process(REQUEST *request)
@@ -71,49 +77,7 @@ static int dhcp_process(REQUEST *request)
vp = pairfind(request->config_items, 270, DHCP_MAGIC_VENDOR);
}
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, 266, DHCP_MAGIC_VENDOR);
- if (giaddr && (giaddr->vp_ipaddr == htonl(INADDR_ANY))) {
- if (pairfind(request->packet->vps, 82, DHCP_MAGIC_VENDOR)) {
- 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, 259, DHCP_MAGIC_VENDOR);
- if (vp) vp->vp_integer++;
-
+ RDEBUG("DHCP: We are not a DHCP relay, discarding");
return 1;
}
@@ -121,49 +85,7 @@ static int dhcp_process(REQUEST *request)
* 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->packet->vps, 266, DHCP_MAGIC_VENDOR);
-
- /*
- * Search for client IP address.
- */
- vp = pairfind(request->packet->vps, 264, DHCP_MAGIC_VENDOR);
- 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, 259, DHCP_MAGIC_VENDOR);
- if (vp && (vp->vp_integer > 0)) vp->vp_integer--;
-
- /*
- * FIXME: Keep original somewhere? If the
- * broadcast flags are set, use them here?
- */
-
+ RDEBUG("DHCP: We are not a DHCP relay, discarding");
return 1;
}
@@ -218,11 +140,11 @@ static int dhcp_process(REQUEST *request)
static int dhcp_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
{
- int rcode, broadcast = 1;
- int on = 1;
+ int rcode, s;
dhcp_socket_t *sock;
RADCLIENT *client;
CONF_PAIR *cp;
+ static char strdhcp[]="dhcp";
rcode = common_socket_parse(cs, this);
if (rcode != 0) return rcode;
@@ -232,31 +154,6 @@ static int dhcp_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
sock = this->data;
/*
- * See whether or not we enable broadcast packets.
- */
- 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 address option: %s\n",
- strerror(errno));
- return -1;
- }
-
- /*
* Undocumented extension for testing without
* destroying your network!
*/
@@ -280,11 +177,56 @@ static int dhcp_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
client->ipaddr.af = AF_INET;
client->ipaddr.ipaddr.ip4addr.s_addr = INADDR_NONE;
client->prefix = 0;
- client->longname = client->shortname = "dhcp";
+ client->longname = client->shortname = strdhcp;
client->secret = client->shortname;
client->nastype = strdup("none");
- return 0;
+ /*
+ * Socket magic.
+ */
+
+ { int on=1;
+ setsockopt(s=this->fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ }
+ { struct ifreq ifr;
+ memset(&ifr,0,sizeof ifr);
+ ifr.ifr_addr.sa_family=AF_INET;
+ strncpy(ifr.ifr_ifrn.ifrn_name,sock->interface,IFNAMSIZ);
+ if(!ioctl(s,SIOCGIFADDR,&ifr))
+ { sock->ipaddr.ipaddr.ip4addr.s_addr
+ =((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr.s_addr;
+ /* do not bind to the interface address, or broadcasts will not
+ * be received
+ */
+ if(!ioctl(s,SIOCGIFNETMASK,&ifr))
+ { sock->netmask.ipaddr.ip4addr.s_addr=
+ ((struct sockaddr_in*)&ifr.ifr_netmask)->sin_addr.s_addr;
+ if(!ioctl(s,SIOCGIFHWADDR,&ifr))
+ { memcpy(sock->hwaddr,ifr.ifr_hwaddr.sa_data,6);
+ sock->hwfamily=ifr.ifr_hwaddr.sa_family;
+ if(!ioctl(s,SIOCGIFINDEX,&ifr))
+ { sock->ifindex=ifr.ifr_ifindex;
+ if((sock->rsocket=s=
+ socket(PF_PACKET,SOCK_DGRAM,htons(ETH_P_IP)))>=0)
+ { struct sockaddr_ll bif;int n=1;
+ bif.sll_family=AF_PACKET;
+ bif.sll_protocol=htons(ETH_P_IP);
+ bif.sll_ifindex=sock->ifindex;
+ if(setsockopt(s,SOL_SOCKET,SO_BROADCAST,(char*)&n,
+ sizeof n)
+ ||bind(s,(struct sockaddr*)&bif,sizeof bif))
+ close(s),sock->rsocket=-1;
+ }
+ return 0;
+ }
+ }
+ }
+ }
+ }
+
+ cf_log_err(cf_sectiontoitem(cs), "Failed to bind DHCP port");
+ return -1;
}
@@ -316,31 +258,31 @@ static int dhcp_socket_recv(rad_listen_t *listener)
/*
- * Send an authentication response packet
+ * Send a DHCP response packet
*/
static int dhcp_socket_send(rad_listen_t *listener, REQUEST *request)
{
dhcp_socket_t *sock;
+ RADIUS_PACKET *reply = request->reply;
rad_assert(request->listener == listener);
rad_assert(listener->send == dhcp_socket_send);
- if (request->reply->code == 0) return 0; /* don't reply */
+ if (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(reply,
+ request->packet->code == reply->code ? NULL : request->packet) < 0)
+ return -1;
sock = listener->data;
if (sock->suppress_responses) return 0;
- return fr_dhcp_send(request->reply);
+ reply->sockfd = listener->fd;
+ reply->src_ipaddr = sock->ipaddr;
+ reply->hash = sock->ifindex;
+ reply->id = sock->rsocket;
+
+ return fr_dhcp_send(reply);
}
@@ -348,7 +290,6 @@ 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);
}
diff --git a/src/main/listen.c b/src/main/listen.c
index e063e69..f692057 100644
--- a/src/main/listen.c
+++ b/src/main/listen.c
@@ -854,7 +854,7 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
/*
* Try IPv4 first
*/
- ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
+ ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_ANY);
rcode = cf_item_parse(cs, "ipaddr", PW_TYPE_IPADDR,
&ipaddr.ipaddr.ip4addr, NULL);
if (rcode < 0) return -1;
@@ -2646,7 +2646,7 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head, int spawn_flag)
/*
* Else look for bind_address and/or listen sections.
*/
- server_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
+ server_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_ANY);
rcode = cf_item_parse(config, "bind_address",
PW_TYPE_IPADDR,
&server_ipaddr.ipaddr.ip4addr, NULL);
--
1.7.5.4
More information about the Freeradius-Devel
mailing list