--- 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