--- 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
I took the liberty to drop all DHCP-relaying code, i.e. freeradius cannot be used as a dhcprelay, freeradius *will* respond properly to a request forwarded through another relay. The DHCP relaying functionality is not really a functionality where there is much to decide or configure. It's best left to a dedicated DHCP relay daemon (they exist, are quite small and efficient). -- Stephen.
On 23 Aug 2011, at 12:52, Stephen R. van den Berg wrote:
I took the liberty to drop all DHCP-relaying code, i.e. freeradius cannot be used as a dhcprelay, freeradius *will* respond properly to a request forwarded through another relay.
The DHCP relaying functionality is not really a functionality where there is much to decide or configure. It's best left to a dedicated DHCP relay daemon (they exist, are quite small and efficient).
I'll leave Alan to comment, but I think the relay code is required for integration with another project... We'll see what he wants to do. -Arran Arran Cudbard-Bell a.cudbardb@freeradius.org RADIUS - Half the complexity of Diameter
Stephen R. van den Berg wrote:
I took the liberty to drop all DHCP-relaying code, i.e. freeradius cannot be used as a dhcprelay, freeradius *will* respond properly to a request forwarded through another relay.
The DHCP relaying functionality is not really a functionality where there is much to decide or configure. It's best left to a dedicated DHCP relay daemon (they exist, are quite small and efficient).
The DHCP relaying code is in FreeRADIUS for a reason. While it doesn't work perfectly yet, there have been a number of requests for it. Alan DeKok.
Alan DeKok wrote:
The DHCP relaying code is in FreeRADIUS for a reason. While it doesn't work perfectly yet, there have been a number of requests for it.
What is the rationale for them not to use dhcp-helper ? -- Stephen. "What do I do when I see someone *extremely gorgeous*? I stare, I smile, and when I get tired... I put the mirror down."
On 23 Aug 2011, at 14:24, Stephen R. van den Berg wrote:
Alan DeKok wrote:
The DHCP relaying code is in FreeRADIUS for a reason. While it doesn't work perfectly yet, there have been a number of requests for it.
What is the rationale for them not to use dhcp-helper ?
Whats the rationale for them not to use another DHCP server? Arran Cudbard-Bell a.cudbardb@freeradius.org RADIUS - Half the complexity of Diameter
Arran Cudbard-Bell wrote:
On 23 Aug 2011, at 14:24, Stephen R. van den Berg wrote:
Alan DeKok wrote:
The DHCP relaying code is in FreeRADIUS for a reason. While it doesn't work perfectly yet, there have been a number of requests for it.
What is the rationale for them not to use dhcp-helper ?
Whats the rationale for them not to use another DHCP server?
It seems that you don't know the answer to my question, but I'll help you with this one: The reason one would not want to use a different DHCP server is because freeradius allows controlling the DHCP server using a RADIUS backend. This allows for more finegrained control over the DHCP responses than most other DHCP servers provide. DHCP relay functionality, however, is a rather dull and simple task. Just about the only configurable thing about a DHCP relay is the IP address of the upstream DHCP server it relays to. Embedding this functionality in a radius server doesn't give you great benefits. A small program like dhcp-helper does the relaying part just fine (at least as good as, if not better than freeradius). -- Stephen. "What do I do when I see someone *extremely gorgeous*? I stare, I smile, and when I get tired... I put the mirror down."
"Stephen R. van den Berg" <srb@cuci.nl> writes:
Just about the only configurable thing about a DHCP relay is the IP address of the upstream DHCP server it relays to.
"To relay or not to relay, that's the question" After deciding that, an intelligent relay may also choose different upstreams based on the received request (e.g. based on option 82 inserted by another relay agent). This does require more backend logic than most dhcp relay agents have. Bjørn
Stephen R. van den Berg wrote:
The reason one would not want to use a different DHCP server is because freeradius allows controlling the DHCP server using a RADIUS backend.
No. FreeRADIUS is (pretty much) no longer just a RADIUS server. It's a generic policy && protocol server. It does RADIUS, DHCP, and VMPS. Other protocols are likely to be added: TACACS+, and maybe ARP. The goal for 3.0 (or maybe 3.1) is to move the protocol-specific code into plugin libraries. The server core will know *nothing* about RADIUS.
This allows for more finegrained control over the DHCP responses than most other DHCP servers provide.
Which is exactly the reason why relaying support should be added, too.
DHCP relay functionality, however, is a rather dull and simple task. Just about the only configurable thing about a DHCP relay is the IP address of the upstream DHCP server it relays to. Embedding this functionality in a radius server doesn't give you great benefits. A small program like dhcp-helper does the relaying part just fine (at least as good as, if not better than freeradius).
dhcp-helper hasn't been updated in 3 years. It's Linux only. It doesn't do policies. FreeRADIUS is portable to many different platforms. It does policies. Having a policy-enabled DHCP relay across many platforms is very useful. Alan DeKok.
"Stephen R. van den Berg" <srb@cuci.nl> writes:
Alan DeKok wrote:
The DHCP relaying code is in FreeRADIUS for a reason. While it doesn't work perfectly yet, there have been a number of requests for it.
What is the rationale for them not to use dhcp-helper ?
I don't use the DHCP functionality in FreeRADIUS at all, but I can imagine that it is useful to let a FreeRADIUS policy decide whether to serve the request locally or to relay it. This is hard to do without an internal relay agent. Much like you can do both proxy and local RADIUS authentication on the same server. You don't really want to have an external RADIUS proxy... Bjørn
Stephen R. van den Berg wrote:
Alan DeKok wrote:
The DHCP relaying code is in FreeRADIUS for a reason. While it doesn't work perfectly yet, there have been a number of requests for it.
What is the rationale for them not to use dhcp-helper ?
For the same reason as adding DHCP to FreeRADIUS. Current DHCP servers && relays have *no* ability to talk to databases, and have limited policy capabilities. FreeRADIUS talks to *anything*, and has highly flexible policies. Alan DeKok.
Alan DeKok wrote:
Stephen R. van den Berg wrote:
Alan DeKok wrote:
The DHCP relaying code is in FreeRADIUS for a reason. While it doesn't work perfectly yet, there have been a number of requests for it.
What is the rationale for them not to use dhcp-helper ?
For the same reason as adding DHCP to FreeRADIUS.
Current DHCP servers && relays have *no* ability to talk to databases, and have limited policy capabilities. FreeRADIUS talks to *anything*, and has highly flexible policies.
So there are applications that actually modify the dhcp request that is being relayed depending on database content? For DHCP requests I can imagine that (obviously), for DHCP relay I find it difficult to come up with a use case that sticks, but maybe my imagination runs short here. -- Stephen.
Stephen R. van den Berg wrote:
So there are applications that actually modify the dhcp request that is being relayed depending on database content?
Yes.
For DHCP requests I can imagine that (obviously), for DHCP relay I find it difficult to come up with a use case that sticks, but maybe my imagination runs short here.
Other people find it useful. Removing code that you don't use but others *do* use is not an option. Alan DeKok.
On 23 Aug 2011, at 15:30, Alan DeKok wrote:
Stephen R. van den Berg wrote:
So there are applications that actually modify the dhcp request that is being relayed depending on database content?
Yes.
For example if you want to direct the response somewhere... -Arran Arran Cudbard-Bell a.cudbardb@freeradius.org RADIUS - Half the complexity of Diameter
participants (4)
-
Alan DeKok -
Arran Cudbard-Bell -
Bjørn Mork -
Stephen R. van den Berg