From: Alan T. DeKok Date: Tue, 29 May 2018 15:10:38 +0000 (-0400) Subject: set src/dst IP for packets appropriately X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=222fca7626f2f2934bad424123088ba7a4e4c280;p=thirdparty%2Ffreeradius-server.git set src/dst IP for packets appropriately --- diff --git a/src/modules/proto_dhcpv4/proto_dhcpv4_udp.c b/src/modules/proto_dhcpv4/proto_dhcpv4_udp.c index bea5dcf628e..9ae11e0e9ee 100644 --- a/src/modules/proto_dhcpv4/proto_dhcpv4_udp.c +++ b/src/modules/proto_dhcpv4/proto_dhcpv4_udp.c @@ -158,7 +158,7 @@ static ssize_t mod_read(void *instance, void **packet_ctx, fr_time_t **recv_time &address->dst_ipaddr, &address->dst_port, &address->if_index, ×tamp); if (data_size < 0) { - DEBUG2("proto_dhcpv4_udp got read error %zd: %s", data_size, fr_strerror()); + DEBUG2("proto_dhvpv4_udp got read error %zd: %s", data_size, fr_strerror()); return data_size; } @@ -214,7 +214,7 @@ static ssize_t mod_write(void *instance, void *packet_ctx, { proto_dhcpv4_udp_t *inst = talloc_get_type_abort(instance, proto_dhcpv4_udp_t); fr_io_track_t *track = talloc_get_type_abort(packet_ctx, fr_io_track_t); - fr_io_address_t address = *track->address; + fr_io_address_t address; int flags; ssize_t data_size; @@ -230,14 +230,26 @@ static ssize_t mod_write(void *instance, void *packet_ctx, rad_assert(track->reply_len == 0); + /* + * Swap src/dst IP/port + */ + address.src_ipaddr = track->address->dst_ipaddr; + address.src_port = track->address->dst_port; + address.dst_ipaddr = track->address->src_ipaddr; + address.dst_port = track->address->src_port; + address.if_index = track->address->if_index; + /* * Figure out which kind of packet we're sending. */ if (!inst->connection) { - uint8_t const *code; + uint8_t const *code, *sid; uint32_t ipaddr; dhcp_packet_t *packet = (dhcp_packet_t *) buffer; dhcp_packet_t *request = (dhcp_packet_t *) track->packet; /* only 20 bytes tho! */ +#ifdef WITH_IFINDEX_IPADDR_RESOLUTION + fr_ipaddr_t primary; +#endif /* * This isn't available in the packet header. @@ -248,6 +260,33 @@ static ssize_t mod_write(void *instance, void *packet_ctx, return 0; } + /* + * Set the source IP of the packet. + * + * - if src_ipaddr is unicast, use that + * - else if socket wasn't bound to *, then use that + * - else if we have if_index, get main IP from that interface and use that. + * - else for offer/ack, look at option 54, for Server Identification and use that + * - else leave source IP as whatever is already in "address.src_ipaddr". + */ + if (inst->src_ipaddr.addr.v4.s_addr != INADDR_ANY) { + address.src_ipaddr = inst->src_ipaddr; + + } else if (inst->ipaddr.addr.v4.s_addr != INADDR_ANY) { + address.src_ipaddr = inst->ipaddr; + +#ifdef WITH_IFINDEX_IPADDR_RESOLUTION + } else if ((address->if_index > 0) && + (fr_ipaddr_from_ifindex(&primary, inst->sockfd, &address.dst_ipaddr.af, + &address.if_index) == 0)) { + address.src_ipaddr = primary; +#endif + } else if (((code[2] == FR_DHCP_OFFER) || (code[2] == FR_DHCP_ACK)) && + ((sid = fr_dhcpv4_packet_get_option(packet, buffer_len, FR_DHCP_DHCP_SERVER_IDENTIFIER)) != NULL) && + (sid[1] == 4)) { + memcpy(&address.src_ipaddr.addr.v4.s_addr, sid + 2, 4); + } + /* * We have GIADDR in the packet, so send it * there. The packet is FROM our IP address and @@ -258,8 +297,7 @@ static ssize_t mod_write(void *instance, void *packet_ctx, if (ipaddr != INADDR_ANY) { address.dst_ipaddr.addr.v4.s_addr = ipaddr; address.dst_port = inst->port; - address.src_ipaddr = inst->src_ipaddr; - address.dst_port = inst->port; + address.src_port = inst->port; /* * Increase the hop count for client @@ -294,7 +332,7 @@ static ssize_t mod_write(void *instance, void *packet_ctx, */ if (((request->flags & FR_DHCP_FLAGS_VALUE_BROADCAST) != 0) && (request->ciaddr == INADDR_ANY)) { - DEBUG("Reply will be broadcast"); + DEBUG("Reply will be broadcast due to client request."); address.dst_ipaddr.addr.v4.s_addr = INADDR_BROADCAST; goto send_reply; } @@ -309,6 +347,17 @@ static ssize_t mod_write(void *instance, void *packet_ctx, goto send_reply; } + /* + * The original packet was unicast to us, such as + * via a relay. We have a unicast destination + * address, so we just use that. + */ + if ((packet->yiaddr == htonl(INADDR_ANY)) && + (address.dst_ipaddr.addr.v4.s_addr != htonl(INADDR_BROADCAST))) { + DEBUG("Reply will be unicast to source IP from original packet."); + goto send_reply; + } + switch (code[2]) { /* * Offers are sent to YIADDR if we @@ -319,11 +368,8 @@ static ssize_t mod_write(void *instance, void *packet_ctx, */ case FR_DHCP_OFFER: /* - * The master_io automatically swaps - * src/dst ip/port before calling us. So - * if we received the request from - * YIADDR, then the reply will - * automatically be sent there as well. + * If the packet was unicast from the + * client, unicast it back. */ if (memcmp(&address.dst_ipaddr.addr.v4.s_addr, &packet->yiaddr, 4) == 0) { DEBUG("Reply will be unicast to YIADDR."); @@ -340,7 +386,7 @@ static ssize_t mod_write(void *instance, void *packet_ctx, #endif } else { - DEBUG("Reply will be broadcast."); + DEBUG("Reply will be broadcast due to OFFER."); address.dst_ipaddr.addr.v4.s_addr = INADDR_BROADCAST; } break; @@ -357,7 +403,7 @@ static ssize_t mod_write(void *instance, void *packet_ctx, * NAKs are broadcast. */ case FR_DHCP_NAK: - DEBUG("Reply will be broadcast."); + DEBUG("Reply will be broadcast due to NAK."); address.dst_ipaddr.addr.v4.s_addr = INADDR_BROADCAST; break; @@ -372,9 +418,9 @@ send_reply: * proto_radius_dhcpv4 takes care of suppressing do-not-respond, etc. */ data_size = udp_send(inst->sockfd, buffer, buffer_len, flags, - &address.dst_ipaddr, address.dst_port, + &address.src_ipaddr, address.src_port, address.if_index, - &address.src_ipaddr, address.src_port); + &address.dst_ipaddr, address.dst_port); /* * This socket is dead. That's an error... @@ -575,6 +621,11 @@ static int mod_bootstrap(void *instance, CONF_SECTION *cs) return -1; } + if (inst->ipaddr.af != AF_INET) { + cf_log_err(cs, "DHCPv4 transport cannot use IPv6 for 'ipaddr'"); + return -1; + } + /* * If src_ipaddr is defined, it must be of the same address family as "ipaddr" */ @@ -584,6 +635,14 @@ static int mod_bootstrap(void *instance, CONF_SECTION *cs) return -1; } + /* + * Set src_ipaddr to INADDR_NONE if not otherwise specified + */ + if (inst->src_ipaddr.af == AF_UNSPEC) { + memset(&inst->src_ipaddr, 0, sizeof(inst->src_ipaddr)); + inst->src_ipaddr.af = AF_INET; + } + if (inst->recv_buff_is_set) { FR_INTEGER_BOUND_CHECK("recv_buff", inst->recv_buff, >=, 32); FR_INTEGER_BOUND_CHECK("recv_buff", inst->recv_buff, <=, INT_MAX);