Skip to content

Commit

Permalink
Consolidate DHCP and RA answers per interface
Browse files Browse the repository at this point in the history
Signed-off-by: DL6ER <[email protected]>
  • Loading branch information
DL6ER committed Dec 9, 2024
1 parent a05128b commit 57c155c
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 67 deletions.
128 changes: 80 additions & 48 deletions src/tools/dhcp-discover.c
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,7 @@ static void print_dhcp_offer(struct in_addr source, struct dhcp_packet_data *off
}

// Add one empty line for readability
printf("\n");
puts("");
}

// receives a DHCP packet
Expand All @@ -543,26 +543,27 @@ static bool receive_dhcp_packet(void *buffer, int buffer_size, const char *iface
address_size = sizeof(struct sockaddr_in);
recv_result = recvfrom(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)address, &address_size);

printf_locked("\n* Received %d bytes from %s @ %s\n", recv_result, inet_ntoa(address->sin_addr), iface);
start_lock();
printf("* Received %d bytes from %s @ %s\n", recv_result, inet_ntoa(address->sin_addr), iface);
#ifdef DEBUG
printf_locked(" after waiting for %f seconds\n", difftime(time(NULL), start_time));
printf(" after waiting for %f seconds\n", difftime(time(NULL), start_time));
#endif
// Return on error
if(recv_result == -1)
{
printf_locked(" recvfrom() failed on %s, error: %s\n", iface, strerror(errno));
printf(" recvfrom() failed on %s, error: %s\n", iface, strerror(errno));
end_lock();
return false;
}

return true;
}

// waits for a DHCPOFFER message from one or more DHCP servers
static void get_dhcp_offer(const int sock, const uint32_t xid, const char *iface, unsigned char *mac)
static unsigned int get_dhcp_offer(const int sock, const uint32_t xid, const char *iface, unsigned char *mac)
{
struct dhcp_packet_data offer_packet;
struct sockaddr_in source;
unsigned int responses = 0;
unsigned int valid_responses = 0;
time_t start_time;
time_t current_time;
Expand All @@ -577,8 +578,6 @@ static void get_dhcp_offer(const int sock, const uint32_t xid, const char *iface

if(!receive_dhcp_packet(&offer_packet, sizeof(offer_packet), iface, sock, start_time, &source))
continue;
else
responses++;

#ifdef DEBUG
printf(" DHCPOFFER XID: %lu (0x%X)\n", (unsigned long) ntohl(offer_packet.xid), ntohl(offer_packet.xid));
Expand All @@ -590,7 +589,7 @@ static void get_dhcp_offer(const int sock, const uint32_t xid, const char *iface
printf(" DHCPOFFER XID (%lu) does not match our DHCPDISCOVER XID (%lu) - ignoring packet (not for us)\n",
(unsigned long) ntohl(offer_packet.xid), (unsigned long) xid);

pthread_mutex_unlock(&lock);
end_lock();
continue;
}

Expand All @@ -609,7 +608,7 @@ static void get_dhcp_offer(const int sock, const uint32_t xid, const char *iface
printf("%02x%s", offer_packet.chaddr[x], x < 5 ? ":" : "");
printf(" (response MAC address)\n");

pthread_mutex_unlock(&lock);
end_lock();
continue;
}

Expand Down Expand Up @@ -657,85 +656,79 @@ static void get_dhcp_offer(const int sock, const uint32_t xid, const char *iface

printf(" DHCP options:\n");
print_dhcp_offer(source.sin_addr, &offer_packet);
pthread_mutex_unlock(&lock);

end_lock();
valid_responses++;
}
if(responses == valid_responses)
printf("DHCP packets received on %s%s%s: %u\n",
cli_bold(), iface, cli_normal(), valid_responses);
else
printf("DHCP packets received on %s%s%s: %u (%u seen for other machines)\n",
cli_bold(), iface, cli_normal(), valid_responses, responses);

#ifdef DEBUG
printf(" Responses seen while scanning: %u\n", responses);
printf(" Responses meant for this machine: %u\n\n", valid_responses);
#endif
return valid_responses;
}

struct thread_info {
char *iface;
int responses;
};

static void *dhcp_discover_iface_v4(void *args)
{
// Get interface details
const char *iface = ((struct ifaddrs*)args)->ifa_name;
char *thread_name = malloc(strlen(iface) + 4);
sprintf(thread_name, "%s-v4", iface);
struct thread_info *tdata = (struct thread_info *)args;
char *thread_name = malloc(strlen(tdata->iface) + 4);
sprintf(thread_name, "%s-v4", tdata->iface);

// Set interface name as thread name
prctl(PR_SET_NAME, thread_name, 0, 0, 0);
free(thread_name);

// Set interface name as thread name
prctl(PR_SET_NAME, iface, 0, 0, 0);
prctl(PR_SET_NAME, tdata->iface, 0, 0, 0);

// create socket for DHCP communications
const int dhcp_socket = create_dhcp_socket(iface);
const int dhcp_socket = create_dhcp_socket(tdata->iface);

// Cannot create socket, likely a permission error
if(dhcp_socket < 0)
goto end_dhcp_discover_iface;
goto end_dhcp_discover_iface_v4;

// get hardware address of client machine
unsigned char mac[MAX_DHCP_CHADDR_LENGTH] = { 0 };
get_hardware_address(dhcp_socket, iface, mac);
get_hardware_address(dhcp_socket, tdata->iface, mac);

// Generate pseudo-random transaction ID
srand((unsigned int)time(NULL));
const uint32_t xid = (uint32_t)random();

// Probe servers on this interface
if(!send_dhcp_discover(dhcp_socket, xid, iface, mac))
goto end_dhcp_discover_iface;
if(!send_dhcp_discover(dhcp_socket, xid, tdata->iface, mac))
goto end_dhcp_discover_iface_v4;

// wait for a DHCPOFFER packet
get_dhcp_offer(dhcp_socket, xid, iface, mac);
tdata->responses = get_dhcp_offer(dhcp_socket, xid, tdata->iface, mac);

end_dhcp_discover_iface:
end_dhcp_discover_iface_v4:
// Close socket if we created one
if(dhcp_socket > 0)
close(dhcp_socket);

pthread_exit(NULL);
// Return the number of responses
pthread_exit(tdata);
}

static void *dhcp_discover_iface_v6(void *args)
{
// Get interface details
const char *iface = ((struct ifaddrs*)args)->ifa_name;
char *thread_name = malloc(strlen(iface) + 4);
sprintf(thread_name, "%s-v6", iface);
struct thread_info *tdata = (struct thread_info *)args;
char *thread_name = malloc(strlen(tdata->iface) + 4);
sprintf(thread_name, "%s-v6", tdata->iface);

// Set interface name as thread name
prctl(PR_SET_NAME, thread_name, 0, 0, 0);
free(thread_name);

// Perform the same scan for DHCPv6
const int responses = dhcpv6_discover_iface(iface, SCAN_TIMEOUT);
if(responses > 0)
printf("RAs received on %s%s%s: %i\n",
cli_bold(), iface, cli_normal(), responses);
tdata->responses = dhcpv6_discover_iface(tdata->iface, SCAN_TIMEOUT);

pthread_exit(NULL);
// Return the number of responses
pthread_exit(tdata);
}

int run_dhcp_discover(void)
Expand All @@ -761,11 +754,12 @@ int run_dhcp_discover(void)
log_ctrl(false, true);

printf("Scanning all your interfaces for DHCP servers and IPv6 routers\n");
printf("Timeout: %d seconds\n", SCAN_TIMEOUT);
printf("Timeout: %d seconds\n\n", SCAN_TIMEOUT);

// Get interface names for available interfaces on this machine
// and launch a thread for each one
pthread_t scanthread[2*MAXTHREADS] = { 0 };
struct thread_info thread_infos[2*MAXTHREADS] = { 0 };
pthread_attr_t attr;
// Initialize thread attributes object with default attribute values
pthread_attr_init(&attr);
Expand Down Expand Up @@ -804,7 +798,8 @@ int run_dhcp_discover(void)
}

// Create a DHCP probing thread for this interface
if(pthread_create(&scanthread[tid], &attr, dhcp_discover_iface_v4, tmp ) != 0)
thread_infos[tid].iface = strdup(tmp->ifa_name);
if(pthread_create(&scanthread[tid], &attr, dhcp_discover_iface_v4, &thread_infos[tid] ) != 0)
{
printf_locked("Unable to launch DHCP thread for interface %s, skipping...",
tmp->ifa_name);
Expand All @@ -813,7 +808,8 @@ int run_dhcp_discover(void)
}

// Create a RA probing thread for this interface
if(pthread_create(&scanthread[2*tid], &attr, dhcp_discover_iface_v6, tmp ) != 0)
thread_infos[MAXTHREADS + tid].iface = thread_infos[tid].iface;
if(pthread_create(&scanthread[MAXTHREADS + tid], &attr, dhcp_discover_iface_v6, &thread_infos[MAXTHREADS + tid] ) != 0)
{
printf_locked("Unable to launch RA thread for interface %s, skipping...",
tmp->ifa_name);
Expand All @@ -832,10 +828,46 @@ int run_dhcp_discover(void)
// Wait for all threads to join back with us
for(tid--; tid > -1; tid--)
{
char *iface = NULL;
unsigned int v4 = 0, v6 = 0;

// Check DHCP (IPv4) thread
if(scanthread[tid] != 0)
pthread_join(scanthread[tid], NULL);
if(scanthread[2*tid] != 0)
pthread_join(scanthread[2*tid], NULL);
{
void *args = NULL;
pthread_join(scanthread[tid], &args);
struct thread_info *tdata = (struct thread_info *)args;
if(tdata != NULL)
{
iface = tdata->iface;
v4 = tdata->responses > 0 ? tdata->responses : 0;
}
}

// Check RA (IPv6) thread
if(scanthread[MAXTHREADS + tid] != 0)
{
void *args = NULL;
pthread_join(scanthread[MAXTHREADS + tid], &args);
struct thread_info *tdata = (struct thread_info *)args;
if(tdata != NULL)
{
iface = tdata->iface;
v6 = tdata->responses > 0 ? tdata->responses : 0;
}
}

// Print results
if(iface != NULL)
{
if(v4 < 1 && v6 < 1)
printf("No answer on %s%s%s\n",
cli_bold(), iface, cli_normal());
else
printf("Received %u DHCP (IPv4) and %u RA (IPv6) answers on %s%s%s\n",
v4, v6, cli_bold(), iface, cli_normal());
free(iface);
}
}

// Free linked-list of interfaces on this client
Expand Down
39 changes: 20 additions & 19 deletions src/tools/dhcpv6-discover.c
Original file line number Diff line number Diff line change
Expand Up @@ -189,15 +189,15 @@ static int parse_prefix(const struct nd_opt_prefix_info *pi, size_t optlen)
if(inet_ntop(AF_INET6, &pi->nd_opt_pi_prefix, str, sizeof (str)) == NULL)
return -1;

printf(" Prefix: %s/%u\n", str, pi->nd_opt_pi_prefix_len);
printf(" - Prefix: %s/%u\n", str, pi->nd_opt_pi_prefix_len);

const uint8_t opt = pi->nd_opt_pi_flags_reserved;
printf(" Valid lifetime: ");
printf(" Valid lifetime: ");
print_u32_time(pi->nd_opt_pi_valid_time);
printf(" Preferred lifetime: ");
printf(" Preferred lifetime: ");
print_u32_time(pi->nd_opt_pi_preferred_time);
printf(" On-link: %s\n", (opt & ND_OPT_PI_FLAG_ONLINK) ? "Yes" : "No");
printf(" Autonomous address conf.: %s\n",(opt & ND_OPT_PI_FLAG_AUTO) ? "Yes" : "No");
printf(" On-link: %s\n", (opt & ND_OPT_PI_FLAG_ONLINK) ? "Yes" : "No");
printf(" Autonomous address conf.: %s\n",(opt & ND_OPT_PI_FLAG_AUTO) ? "Yes" : "No");

return 0;
}
Expand Down Expand Up @@ -276,9 +276,9 @@ static int parse_route(const uint8_t *opt)
if(inet_ntop (AF_INET6, &dst, str, sizeof (str)) == NULL)
return -1;

printf(" Route: %s/%"PRIu8"\n", str, plen);
printf(" Route preference: %s\n", parse_pref(opt[3]));
printf(" Route lifetime: ");
printf(" - Route: %s/%"PRIu8"\n", str, plen);
printf(" Route preference: %s\n", parse_pref(opt[3]));
printf(" Route lifetime: ");
print_u8_time(opt);
return 0;
}
Expand Down Expand Up @@ -374,7 +374,7 @@ static int parse_dnssl(const uint8_t *opt)

}

printf("\n");
puts("");

printf(" DNS search list lifetime: ");
print_u8_time(opt);
Expand Down Expand Up @@ -453,26 +453,26 @@ static int parse_ra(const uint8_t *buf, size_t len)
else
puts("undefined");

printf(" Stateful address conf.: %s\n", (ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) ? "Yes" : "No");
printf(" Stateful other conf.: %s\n", (ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) ? "Yes" : "No");
printf(" Mobile home agent: %s\n", (ra->nd_ra_flags_reserved & ND_RA_FLAG_HOME_AGENT) ? "Yes" : "No");
printf(" Router preference: %s\n", parse_pref(ra->nd_ra_flags_reserved));
printf(" Neighbor discovery proxy: %s\n", (ra->nd_ra_flags_reserved & 0x04) ? "Yes" : "No");
printf(" Stateful address conf.: %s\n", (ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) ? "Yes" : "No");
printf(" Stateful other conf.: %s\n", (ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) ? "Yes" : "No");
printf(" Mobile home agent: %s\n", (ra->nd_ra_flags_reserved & ND_RA_FLAG_HOME_AGENT) ? "Yes" : "No");
printf(" Router preference: %s\n", parse_pref(ra->nd_ra_flags_reserved));
printf(" Neighbor discovery proxy: %s\n", (ra->nd_ra_flags_reserved & 0x04) ? "Yes" : "No");

/* Router lifetime */
const uint16_t router_lifetime = ntohs(ra->nd_ra_router_lifetime);
printf(" Router lifetime: %u s\n", router_lifetime);
printf(" Router lifetime: %u s\n", router_lifetime);

/* ND Reachable time */
const uint16_t reachable = ntohs(ra->nd_ra_reachable);
printf(" Reachable time: ");
printf(" Reachable time: ");
if(reachable != 0)
printf("%u ms\n", reachable);
else
puts("N/A");

/* ND Retransmit time */
printf(" Retransmit time: ");
printf(" Retransmit time: ");
const uint16_t retransmit = ntohl (ra->nd_ra_retransmit);
if (retransmit != 0)
printf("%u ms\n", retransmit);
Expand Down Expand Up @@ -668,7 +668,8 @@ static ssize_t recv_adv(int fd, const struct sockaddr_in6 *tgt, const char *ifna
{
// Calculate the remaining time
val = (end.tv_sec - now.tv_sec) * 1000 + (int)((end.tv_nsec - now.tv_nsec) / 1000000);
if (val < 0) val = 0;
if (val <= 0) // Timeout
return responses;
}

// Wait for reply (retries on EINTR)
Expand Down Expand Up @@ -705,10 +706,10 @@ static ssize_t recv_adv(int fd, const struct sockaddr_in6 *tgt, const char *ifna
// Print the received packet's size and the source address
char str[INET6_ADDRSTRLEN] = { 0 };
inet_ntop(AF_INET6, &addr.sin6_addr, str,sizeof (str));
start_lock();
printf("* Received %zd bytes from %s @ %s\n", val, str, ifname);

// Parse the Router Advertisement
start_lock();
if(parse_ra(buf, val) == 0)
responses++;
end_lock();
Expand Down

0 comments on commit 57c155c

Please sign in to comment.