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