From 45aa667519ca39c58116bd02ce3d0c7a3968bd9e Mon Sep 17 00:00:00 2001 From: Arran Cudbard-Bell Date: Mon, 4 Mar 2013 21:25:37 -0500 Subject: [PATCH] DHCP fixes from v2.1.x --- src/lib/dhcp.c | 408 ++++++++++++++++++++++--------------------------- 1 file changed, 179 insertions(+), 229 deletions(-) diff --git a/src/lib/dhcp.c b/src/lib/dhcp.c index a4924b95b12..ee87540a384 100644 --- a/src/lib/dhcp.c +++ b/src/lib/dhcp.c @@ -28,11 +28,6 @@ RCSID("$Id$") #include #include -/* - * This doesn't appear to work right now. - */ -#undef WITH_UDPFROMTO - #ifdef WITH_DHCP #include @@ -213,7 +208,6 @@ static uint8_t *dhcp_get_option(dhcp_packet_t *packet, size_t packet_size, */ RADIUS_PACKET *fr_dhcp_recv(int sockfd) { - ssize_t len; uint32_t magic; struct sockaddr_storage src; struct sockaddr_storage dst; @@ -241,23 +235,23 @@ RADIUS_PACKET *fr_dhcp_recv(int sockfd) sizeof_src = sizeof(src); #ifdef WITH_UDPFROMTO sizeof_dst = sizeof(dst); - len = recvfromto(sockfd, packet->data, MAX_PACKET_SIZE, 0, - (struct sockaddr *)&src, &sizeof_src, - (struct sockaddr *)&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 - len = recvfrom(sockfd, packet->data, MAX_PACKET_SIZE, 0, - (struct sockaddr *)&src, &sizeof_src); + packet->data_len = recvfrom(sockfd, packet->data, MAX_PACKET_SIZE, 0, + (struct sockaddr *)&src, &sizeof_src); #endif - if (len <= 0) { + if (packet->data_len <= 0) { fr_strerror_printf("Failed reading DHCP socket: %s", strerror(errno)); rad_free(&packet); return NULL; } - if (len < MIN_PACKET_SIZE) { - fr_strerror_printf("DHCP packet is too small (%d < %d)", - (int) len, MIN_PACKET_SIZE); + if (packet->data_len < MIN_PACKET_SIZE) { + fr_strerror_printf("DHCP packet is too small (%zu < %d)", + packet->data_len, MIN_PACKET_SIZE); rad_free(&packet); return NULL; } @@ -276,8 +270,6 @@ RADIUS_PACKET *fr_dhcp_recv(int sockfd) return NULL; } - packet->data_len = len; - memcpy(&magic, packet->data + 236, 4); magic = ntohl(magic); if (magic != DHCP_OPTION_MAGIC_NUMBER) { @@ -394,22 +386,47 @@ int fr_dhcp_send(RADIUS_PACKET *packet) #ifdef WITH_UDPFROMTO struct sockaddr_storage src; socklen_t sizeof_src; + + fr_ipaddr2sockaddr(&packet->src_ipaddr, packet->src_port, + &src, &sizeof_src); #endif - if (!fr_ipaddr2sockaddr(&packet->dst_ipaddr, packet->dst_port, - &dst, &sizeof_dst)) { - return -1; - } + 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! - */ + if (fr_debug_flag > 1) { + char type_buf[64]; + const char *name = type_buf; +#ifdef WITH_UDPFROMTO + char src_ip_buf[256]; +#endif + char dst_ip_buf[256]; + + if ((packet->code >= PW_DHCP_DISCOVER) && + (packet->code <= PW_DHCP_INFORM)) { + name = dhcp_message_types[packet->code - PW_DHCP_OFFSET]; + } else { + snprintf(type_buf, sizeof(type_buf), "%d", + packet->code - PW_DHCP_OFFSET); + } + + DEBUG( +#ifdef WITH_UDPFROMTO + "Sending %s of id %08x from %s:%d to %s:%d\n", +#else + "Sending %s of id %08x to %s:%d\n", +#endif + name, (unsigned int) packet->id, +#ifdef WITH_UDPFROMTO + inet_ntop(packet->src_ipaddr.af, + &packet->src_ipaddr.ipaddr, + src_ip_buf, sizeof(src_ip_buf)), + packet->src_port, +#endif + inet_ntop(packet->dst_ipaddr.af, + &packet->dst_ipaddr.ipaddr, + dst_ip_buf, sizeof(dst_ip_buf)), + packet->dst_port); } #ifndef WITH_UDPFROMTO @@ -419,14 +436,9 @@ int fr_dhcp_send(RADIUS_PACKET *packet) return sendto(packet->sockfd, packet->data, packet->data_len, 0, (struct sockaddr *)&dst, sizeof_dst); #else - if (!fr_ipaddr2sockaddr(&packet->src_ipaddr, packet->src_port, - &src, &sizeof_src)) { - return -1; - } - return sendfromto(packet->sockfd, - packet->data, packet->data_len, 0, - (struct sockaddr *)&src, sizeof_src, + return sendfromto(packet->sockfd, packet->data, packet->data_len, 0, + (struct sockaddr *)&src, sizeof_src, (struct sockaddr *)&dst, sizeof_dst); #endif } @@ -646,7 +658,14 @@ ssize_t fr_dhcp_decode_options(uint8_t *data, size_t len, VALUE_PAIR **head) /* * Hack for ease of use. */ - if (fr_dhcp_attr2vp(vp, p, alen) < 0) { + if ((da->vendor == DHCP_MAGIC_VENDOR) && + (da->attr == 61) && !da->flags.array && + (alen == 7) && (*p == 1) && (num_entries == 1)) { + vp->type = PW_TYPE_ETHERNET; + memcpy(vp->vp_octets, p + 1, 6); + vp->length = alen; + + } else if (fr_dhcp_attr2vp(vp, p, alen) < 0) { pairfree(&vp); pairfree(head); return -1; @@ -704,7 +723,7 @@ int fr_dhcp_decode(RADIUS_PACKET *packet) return -1; } - if ((i == 11) && + if ((i == 11) && (packet->data[1] == 1) && (packet->data[2] != 6)) { fr_strerror_printf("chaddr of incorrect length for ethernet"); @@ -770,8 +789,8 @@ int fr_dhcp_decode(RADIUS_PACKET *packet) */ /* - * Nothing uses tail after this call, if it does in the future - * it'll need to find the new tail... + * Nothing uses tail after this call, if it does in the future + * it'll need to find the new tail... */ if (fr_dhcp_decode_options(packet->data + 240, packet->data_len - 240, tail) < 0) { @@ -804,7 +823,7 @@ int fr_dhcp_decode(RADIUS_PACKET *packet) * Reply should be broadcast. */ if (vp) vp->vp_integer |= 0x8000; - packet->data[10] |= 0x80; + packet->data[10] |= 0x80; } } } @@ -864,7 +883,7 @@ static int attr_cmp(const void *one, const void *two) * Relay-Agent is last */ if (((*a)->da->attr == 82) && - (*b)->da->attr != 82) return +1; + (*b)->da->attr != 82) return 1; return ((*a)->da->attr - (*b)->da->attr); } @@ -1010,144 +1029,66 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original) VALUE_PAIR *vp; uint32_t lvalue, mms; size_t dhcp_size, length; - dhcp_packet_t *dhcp; +#ifndef NDEBUG + const char *name; +# ifdef WITH_UDPFROMTO + char src_ip_buf[256]; +# endif + char dst_ip_buf[256]; +#endif if (packet->data) return 0; packet->data = malloc(MAX_PACKET_SIZE); - if (!packet->data) return -1; - packet->data_len = MAX_PACKET_SIZE; + memset(packet->data, 0, packet->data_len); + /* XXX Ugly ... should be set by the caller */ if (packet->code == 0) packet->code = PW_DHCP_NAK; - /* - * If there's a request, use it as a template. - * Otherwise, assume that the caller has set up - * everything appropriately. - */ - if (original) { - packet->dst_ipaddr.af = AF_INET; - packet->src_ipaddr.af = AF_INET; - - packet->dst_port = original->src_port; - packet->src_port = original->dst_port; - - /* - * Note that for DHCP, we NEVER send the response - * to the source IP address of the request. It - * may have traversed multiple relays, and we - * need to send the request to the relay closest - * to the client. - * - * if giaddr, send to giaddr. - * if NAK, send broadcast. - * if broadcast flag, send broadcast. - * if ciaddr is empty, send broadcast. - * otherwise unicast to ciaddr. - */ - - /* - * FIXME: alignment issues. We likely don't want to - * 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) || - (dhcp->ciaddr == htonl(INADDR_ANY))) { - /* - * The kernel will take care of sending it to - * the broadcast MAC. - */ - packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST); - - } else { - /* - * It was broadcast to us: we need to - * broadcast the response. - */ - if (packet->src_ipaddr.ipaddr.ip4addr.s_addr != dhcp->ciaddr) { - packet->offset = 0; - } - packet->dst_ipaddr.ipaddr.ip4addr.s_addr = dhcp->ciaddr; - } - - /* - * 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); - } +#ifndef NDEBUG + if ((packet->code >= PW_DHCP_DISCOVER) && + (packet->code <= PW_DHCP_INFORM)) { + name = dhcp_message_types[packet->code - PW_DHCP_OFFSET]; } else { - memset(packet->data, 0, packet->data_len); - } - - if (fr_debug_flag > 1) { - char type_buf[64]; - const char *name = type_buf; - char src_ip_buf[256], dst_ip_buf[256]; - - if ((packet->code >= PW_DHCP_DISCOVER) && - (packet->code <= PW_DHCP_INFORM)) { - name = dhcp_message_types[packet->code - PW_DHCP_OFFSET]; - } else { - snprintf(type_buf, sizeof(type_buf), "%d", - packet->code - PW_DHCP_OFFSET); - } - - DEBUG("Sending %s of id %08x from %s:%d to %s:%d\n", - name, (unsigned int) packet->id, - inet_ntop(packet->src_ipaddr.af, - &packet->src_ipaddr.ipaddr, - src_ip_buf, sizeof(src_ip_buf)), - packet->src_port, - inet_ntop(packet->dst_ipaddr.af, - &packet->dst_ipaddr.ipaddr, - dst_ip_buf, sizeof(dst_ip_buf)), - packet->dst_port); - - if (fr_debug_flag) { - for (i = 256; i < 269; i++) { - vp = pairfind(packet->vps, i, DHCP_MAGIC_VENDOR, TAG_ANY); - if (!vp) continue; - - debug_pair(vp); - } - } + name = "?Unknown?"; } +#endif + + DEBUG( +#ifdef WITH_UDPFROMTO + "Encoding %s of id %08x from %s:%d to %s:%d\n", +#else + "Encoding %s of id %08x to %s:%d\n", +#endif + name, (unsigned int) packet->id, +#ifdef WITH_UDPFROMTO + inet_ntop(packet->src_ipaddr.af, + &packet->src_ipaddr.ipaddr, + src_ip_buf, sizeof(src_ip_buf)), + packet->src_port, +#endif + inet_ntop(packet->dst_ipaddr.af, + &packet->dst_ipaddr.ipaddr, + dst_ip_buf, sizeof(dst_ip_buf)), + packet->dst_port); p = packet->data; mms = DEFAULT_PACKET_SIZE; /* maximum message size */ - if (original) { - /* - * Clients can request a LARGER size, but not a - * smaller one. They also cannot request a size - * larger than MTU. - */ - vp = pairfind(original->vps, 57, DHCP_MAGIC_VENDOR, TAG_ANY); - if (vp && (vp->vp_integer > mms)) { - mms = vp->vp_integer; - - if (mms > MAX_PACKET_SIZE) mms = MAX_PACKET_SIZE; - } + /* + * Clients can request a LARGER size, but not a + * smaller one. They also cannot request a size + * larger than MTU. + */ + + /* DHCP-DHCP-Maximum-Msg-Size */ + vp = pairfind(packet->vps, 57, DHCP_MAGIC_VENDOR, TAG_ANY); + if (vp && (vp->vp_integer > mms)) { + mms = vp->vp_integer; + + if (mms > MAX_PACKET_SIZE) mms = MAX_PACKET_SIZE; } /* @@ -1193,7 +1134,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original) } } else { /* we don't support this type! */ fr_strerror_printf("DHCP-Authentication %d unsupported", - vp->vp_octets[0]); + vp->vp_octets[0]); } } @@ -1201,101 +1142,108 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original) if (vp) { *p++ = vp->vp_integer & 0xff; } else { - if (!original) { - *p++ = 1; /* client message */ - } else { - *p++ = 2; /* server message */ - } + *p++ = 1; /* client message */ + } + + /* DHCP-Hardware-Type */ + if ((vp = pairfind(packet->vps, 257, DHCP_MAGIC_VENDOR, TAG_ANY))) { + *p++ = vp->vp_integer & 0xFF; + } else { + *p++ = 1; /* hardware type = ethernet */ } - *p++ = 1; /* hardware type = ethernet */ - *p++ = 6; /* 6 bytes of ethernet */ - vp = pairfind(packet->vps, 259, DHCP_MAGIC_VENDOR, TAG_ANY); - if (vp) { - *p++ = vp->vp_integer & 0xff; + /* DHCP-Hardware-Address-Length */ + if ((vp = pairfind(packet->vps, 258, DHCP_MAGIC_VENDOR, TAG_ANY))) { + *p++ = vp->vp_integer & 0xFF; } else { - *p++ = 0; /* hops */ + *p++ = 6; /* 6 bytes of ethernet */ } - if (original) { /* Xid */ - memcpy(p, original->data + 4, 4); + /* DHCP-Hop-Count */ + if ((vp = pairfind(packet->vps, 259, DHCP_MAGIC_VENDOR, TAG_ANY))) { + *p = vp->vp_integer & 0xff; + } + p++; + + /* DHCP-Transaction-Id */ + if ((vp = pairfind(packet->vps, 260, DHCP_MAGIC_VENDOR, TAG_ANY))) { + lvalue = htonl(vp->vp_integer); } else { lvalue = fr_rand(); - memcpy(p, &lvalue, 4); } + memcpy(p, &lvalue, 4); p += 4; - memset(p, 0, 2); /* secs are zero */ + /* DHCP-Number-of-Seconds */ + if ((vp = pairfind(packet->vps, 261, DHCP_MAGIC_VENDOR, TAG_ANY))) { + lvalue = htonl(vp->vp_integer); + memcpy(p, &lvalue, 2); + } p += 2; - if (original) { - memcpy(p, original->data + 10, 6); /* copy flags && ciaddr */ + /* DHCP-Flags */ + if ((vp = pairfind(packet->vps, 262, DHCP_MAGIC_VENDOR, TAG_ANY))) { + lvalue = htons(vp->vp_integer); + memcpy(p, &lvalue, 2); } + p += 2; - /* - * Allow the admin to set the broadcast flag. - */ - vp = pairfind(packet->vps, 262, DHCP_MAGIC_VENDOR, TAG_ANY); - if (vp) { - p[0] |= (vp->vp_integer & 0xff00) >> 8; - p[1] |= (vp->vp_integer & 0xff); + /* DHCP-Client-IP-Address */ + if ((vp = pairfind(packet->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY))) { + memcpy(p, &vp->vp_ipaddr, 4); } + p += 4; - p += 6; - - /* - * Set client IP address. - */ - vp = pairfind(packet->vps, 264, DHCP_MAGIC_VENDOR, TAG_ANY); /* Your IP address */ - if (vp) { + /* DHCP-Your-IP-address */ + if ((vp = pairfind(packet->vps, 264, DHCP_MAGIC_VENDOR, TAG_ANY))) { lvalue = vp->vp_ipaddr; } else { lvalue = htonl(INADDR_ANY); } - memcpy(p, &lvalue, 4); /* your IP address */ + memcpy(p, &lvalue, 4); p += 4; - vp = pairfind(packet->vps, 265, DHCP_MAGIC_VENDOR, TAG_ANY); /* server IP address */ - if (!vp) vp = pairfind(packet->vps, 54, DHCP_MAGIC_VENDOR, TAG_ANY); /* identifier */ - if (vp) { + /* DHCP-Server-IP-Address */ + vp = pairfind(packet->vps, 265, DHCP_MAGIC_VENDOR, TAG_ANY); + + /* DHCP-DHCP-Server-Identifier */ + if (!vp && (vp = pairfind(packet->vps, 54, DHCP_MAGIC_VENDOR, TAG_ANY))) { lvalue = vp->vp_ipaddr; } else { lvalue = htonl(INADDR_ANY); } - memcpy(p, &lvalue, 4); /* Server IP address */ + memcpy(p, &lvalue, 4); p += 4; - if (original) { - memcpy(p, original->data + 24, 4); /* copy gateway IP address */ + /* + * DHCP-Gateway-IP-Address + */ + if ((vp = pairfind(packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY))) { + lvalue = vp->vp_ipaddr; } else { - vp = pairfind(packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); - if (vp) { - lvalue = vp->vp_ipaddr; - } else { - lvalue = htonl(INADDR_NONE); - } - memcpy(p, &lvalue, 4); + lvalue = htonl(INADDR_ANY); } + memcpy(p, &lvalue, 4); p += 4; - if (original) { - memcpy(p, original->data + 28, DHCP_CHADDR_LEN); - } else { - vp = pairfind(packet->vps, 267, DHCP_MAGIC_VENDOR, TAG_ANY); - if (vp) { - if (vp->length > DHCP_CHADDR_LEN) { - memcpy(p, vp->vp_octets, DHCP_CHADDR_LEN); - } else { - memcpy(p, vp->vp_octets, vp->length); - } + /* DHCP-Client-Hardware-Address */ + if ((vp = pairfind(packet->vps, 267, DHCP_MAGIC_VENDOR, TAG_ANY))) { + if (vp->length > DHCP_CHADDR_LEN) { + memcpy(p, vp->vp_octets, DHCP_CHADDR_LEN); + } else { + memcpy(p, vp->vp_octets, vp->length); } } p += DHCP_CHADDR_LEN; - /* - * Zero our sname && filename fields. - */ - memset(p, 0, DHCP_SNAME_LEN + DHCP_FILE_LEN); + /* DHCP-Server-Host-Name */ + if ((vp = pairfind(packet->vps, 268, DHCP_MAGIC_VENDOR, TAG_ANY))) { + if (vp->length > DHCP_SNAME_LEN) { + memcpy(p, vp->vp_strvalue, DHCP_SNAME_LEN); + } else { + memcpy(p, vp->vp_strvalue, vp->length); + } + } p += DHCP_SNAME_LEN; /* @@ -1307,8 +1255,9 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original) * 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, TAG_ANY); - if (vp) { + + /* DHCP-Boot-Filename */ + if ((vp = pairfind(packet->vps, 269, DHCP_MAGIC_VENDOR, TAG_ANY))) { if (vp->length > DHCP_FILE_LEN) { memcpy(p, vp->vp_strvalue, DHCP_FILE_LEN); } else { @@ -1317,7 +1266,8 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original) } p += DHCP_FILE_LEN; - lvalue = htonl(DHCP_OPTION_MAGIC_NUMBER); /* DHCP magic number */ + /* DHCP magic number */ + lvalue = htonl(DHCP_OPTION_MAGIC_NUMBER); memcpy(p, &lvalue, 4); p += 4; -- 2.47.3