static void stop_flow(struct request_stop_flow *request) { DEBUG_MSG(LOG_DEBUG, "stop_flow forcefully unlocked mutex"); pthread_mutex_unlock(&mutex); if (request->flow_id == -1) { /* Stop all flows */ const struct list_node *node = fg_list_front(&flows); while (node) { struct flow *flow = node->data; node = node->next; flow->statistics[FINAL].has_tcp_info = get_tcp_info(flow, &flow->statistics[FINAL].tcp_info) ? 0 : 1; flow->pmtu = get_pmtu(flow->fd); if (flow->settings.reporting_interval) report_flow(flow, INTERVAL); report_flow(flow, FINAL); uninit_flow(flow); remove_flow(flow); } return; } const struct list_node *node = fg_list_front(&flows); while (node) { struct flow *flow = node->data; node = node->next; if (flow->id != request->flow_id) continue; /* On Other OSes than Linux or FreeBSD, tcp_info will contain all zeroes */ flow->statistics[FINAL].has_tcp_info = get_tcp_info(flow, &flow->statistics[FINAL].tcp_info) ? 0 : 1; flow->pmtu = get_pmtu(flow->fd); if (flow->settings.reporting_interval) report_flow(flow, INTERVAL); report_flow(flow, FINAL); uninit_flow(flow); remove_flow(flow); return; } request_error(&request->r, "Unknown flow id"); }
static void stop_flow(struct _request_stop_flow *request) { DEBUG_MSG(LOG_DEBUG, "stop_flow forcefully unlocked mutex"); pthread_mutex_unlock(&mutex); if (request->flow_id == -1) { /* Stop all flows */ for (unsigned int i = 0; i < num_flows; i++) { struct _flow *flow = &flows[i]; flow->statistics[FINAL].has_tcp_info = get_tcp_info(flow, &flow->statistics[FINAL].tcp_info) ? 0 : 1; flow->pmtu = get_pmtu(flow->fd); if (flow->settings.reporting_interval) report_flow(flow, INTERVAL); report_flow(flow, FINAL); uninit_flow(flow); remove_flow(i); } return; } for (unsigned int i = 0; i < num_flows; i++) { struct _flow *flow = &flows[i]; if (flow->id != request->flow_id) continue; /* On Other OSes than Linux or FreeBSD, tcp_info will contain all zeroes */ flow->statistics[FINAL].has_tcp_info = get_tcp_info(flow, &flow->statistics[FINAL].tcp_info) ? 0 : 1; flow->pmtu = get_pmtu(flow->fd); if (flow->settings.reporting_interval) report_flow(flow, INTERVAL); report_flow(flow, FINAL); uninit_flow(flow); remove_flow(i); return; } request_error(&request->r, "Unknown flow id"); }
static int prepare_rfds(struct timespec *now, struct _flow *flow, fd_set *rfds) { int rc = 0; if (!flow_in_delay(now, flow, READ) && !flow_sending(now, flow, READ)) { if (!flow->finished[READ] && flow->settings.shutdown) { warnx("server flow %u missed to shutdown", flow->id); rc = shutdown(flow->fd, SHUT_RD); if (rc == -1) warn("shutdown SHUT_RD failed"); flow->finished[READ] = 1; } } if (flow->source_settings.late_connect && !flow->connect_called ) { DEBUG_MSG(LOG_ERR, "late connecting test socket for flow %d " "after %.3fs delay", flow->id, flow->settings.delay[WRITE]); rc = connect(flow->fd, flow->addr, flow->addr_len); if (rc == -1 && errno != EINPROGRESS) { flow_error(flow, "Connect failed: %s", strerror(errno)); return -1; } flow->connect_called = 1; flow->pmtu = get_pmtu(flow->fd); } /* Altough the server flow might be finished we keep the socket in * rfd in order to check for buggy servers */ if (flow->connect_called && !flow->finished[READ]) { DEBUG_MSG(LOG_DEBUG, "adding sock of flow %d to rfds", flow->id); FD_SET(flow->fd, rfds); } return 0; }
static void process_select(fd_set *rfds, fd_set *wfds, fd_set *efds) { unsigned int i = 0; while (i < num_flows) { struct _flow *flow = &flows[i]; DEBUG_MSG(LOG_DEBUG, "processing pselect() for flow %d", flow->id); if (flow->listenfd_data != -1 && FD_ISSET(flow->listenfd_data, rfds)) { DEBUG_MSG(LOG_DEBUG, "ready for accept"); if (flow->state == GRIND_WAIT_ACCEPT) { if (accept_data(flow) == -1) { DEBUG_MSG(LOG_ERR, "accept_data() " "failed"); goto remove; } } } if (flow->fd != -1) { if (FD_ISSET(flow->fd, efds)) { int error_number, rc; socklen_t error_number_size = sizeof(error_number); DEBUG_MSG(LOG_DEBUG, "sock of flow %d in efds", flow->id); rc = getsockopt(flow->fd, SOL_SOCKET, SO_ERROR, (void *)&error_number, &error_number_size); if (rc == -1) { warn("failed to get errno for" "non-blocking connect"); goto remove; } if (error_number != 0) { warnc(error_number, "connect"); goto remove; } } if (FD_ISSET(flow->fd, wfds)) if (write_data(flow) == -1) { DEBUG_MSG(LOG_ERR, "write_data() failed"); goto remove; } if (FD_ISSET(flow->fd, rfds)) if (read_data(flow) == -1) { DEBUG_MSG(LOG_ERR, "read_data() failed"); goto remove; } } i++; continue; remove: if (flow->fd != -1) { flow->statistics[FINAL].has_tcp_info = get_tcp_info(flow, &flow->statistics[FINAL].tcp_info) ? 0 : 1; } flow->pmtu = get_pmtu(flow->fd); report_flow(flow, FINAL); uninit_flow(flow); remove_flow(i); DEBUG_MSG(LOG_ERR, "removed flow %d", flow->id); } }
/* * Prepare a report. type is either INTERVAL or FINAL */ static void report_flow(struct _flow* flow, int type) { DEBUG_MSG(LOG_DEBUG, "report_flow called for flow %d (type %d)", flow->id, type); struct _report* report = (struct _report*)malloc(sizeof(struct _report)); report->id = flow->id; report->type = type; if (type == INTERVAL) report->begin = flow->last_report_time; else report->begin = flow->first_report_time; gettime(&report->end); flow->last_report_time = report->end; /* abort if we were scheduled way to early for a interval report */ if (time_diff(&report->begin,&report->end) < 0.2 * flow->settings.reporting_interval && type == INTERVAL){ free(report); return; } report->bytes_read = flow->statistics[type].bytes_read; report->bytes_written = flow->statistics[type].bytes_written; report->request_blocks_read = flow->statistics[type].request_blocks_read; report->response_blocks_read = flow->statistics[type].response_blocks_read; report->request_blocks_written = flow->statistics[type].request_blocks_written; report->response_blocks_written = flow->statistics[type].response_blocks_written; report->rtt_min = flow->statistics[type].rtt_min; report->rtt_max = flow->statistics[type].rtt_max; report->rtt_sum = flow->statistics[type].rtt_sum; report->iat_min = flow->statistics[type].iat_min; report->iat_max = flow->statistics[type].iat_max; report->iat_sum = flow->statistics[type].iat_sum; report->delay_min = flow->statistics[type].delay_min; report->delay_max = flow->statistics[type].delay_max; report->delay_sum = flow->statistics[type].delay_sum; /* Currently this will only contain useful information on Linux * and FreeBSD */ report->tcp_info = flow->statistics[type].tcp_info; if (flow->fd != -1) { /* Get latest MTU */ flow->pmtu = get_pmtu(flow->fd); report->pmtu = flow->pmtu; if (type == FINAL) report->imtu = get_imtu(flow->fd); else report->imtu = 0; } else { report->imtu = 0; report->pmtu = 0; } /* Add status flags to report */ report->status = 0; if (flow->statistics[type].bytes_read == 0) { if (flow_in_delay(&report->end, flow, READ)) report->status |= 'd'; else if (flow_sending(&report->end, flow, READ)) report->status |= 'l'; else if (flow->settings.duration[READ] == 0) report->status |= 'o'; else report->status |= 'f'; } else { if (!flow_sending(&report->end, flow, READ) && !flow->finished) report->status |= 'c'; else report->status |= 'n'; } report->status <<= 8; if (flow->statistics[type].bytes_written == 0) { if (flow_in_delay(&report->end, flow, WRITE)) report->status |= 'd'; else if (flow_sending(&report->end, flow, WRITE)) report->status |= 'l'; else if (flow->settings.duration[WRITE] == 0) report->status |= 'o'; else report->status |= 'f'; } else { if (!flow_sending(&report->end, flow, WRITE) && !flow->finished) report->status |= 'c'; else report->status |= 'n'; } /* New report interval, reset old data */ if (type == INTERVAL) { flow->statistics[INTERVAL].bytes_read = 0; flow->statistics[INTERVAL].bytes_written = 0; flow->statistics[INTERVAL].request_blocks_read = 0; flow->statistics[INTERVAL].response_blocks_read = 0; flow->statistics[INTERVAL].request_blocks_written = 0; flow->statistics[INTERVAL].response_blocks_written = 0; flow->statistics[INTERVAL].rtt_min = FLT_MAX; flow->statistics[INTERVAL].rtt_max = FLT_MIN; flow->statistics[INTERVAL].rtt_sum = 0.0F; flow->statistics[INTERVAL].iat_min = FLT_MAX; flow->statistics[INTERVAL].iat_max = FLT_MIN; flow->statistics[INTERVAL].iat_sum = 0.0F; flow->statistics[INTERVAL].delay_min = FLT_MAX; flow->statistics[INTERVAL].delay_max = FLT_MIN; flow->statistics[INTERVAL].delay_sum = 0.0F; } add_report(report); DEBUG_MSG(LOG_DEBUG, "report_flow finished for flow %d (type %d)", flow->id, type); }
static int prepare_fds() { DEBUG_MSG(LOG_DEBUG, "prepare_fds() called, num_flows: %d", num_flows); unsigned int i = 0; FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); FD_SET(daemon_pipe[0], &rfds); maxfd = daemon_pipe[0]; struct timespec now; gettime(&now); while (i < num_flows) { struct _flow *flow = &flows[i++]; if (started && (flow->finished[READ] || !flow->settings.duration[READ] || (!flow_in_delay(&now, flow, READ) && !flow_sending(&now, flow, READ))) && (flow->finished[WRITE] || !flow->settings.duration[WRITE] || (!flow_in_delay(&now, flow, WRITE) && !flow_sending(&now, flow, WRITE)))) { /* On Other OSes than Linux or FreeBSD, tcp_info will contain all zeroes */ flow->statistics[FINAL].has_tcp_info = get_tcp_info(flow, &flow->statistics[FINAL].tcp_info) ? 0 : 1; flow->pmtu = get_pmtu(flow->fd); if (flow->settings.reporting_interval) report_flow(flow, INTERVAL); report_flow(flow, FINAL); uninit_flow(flow); remove_flow(--i); continue; } if (flow->state == GRIND_WAIT_ACCEPT && flow->listenfd_data != -1) { FD_SET(flow->listenfd_data, &rfds); maxfd = MAX(maxfd, flow->listenfd_data); } if (!started) continue; if (flow->fd != -1) { FD_SET(flow->fd, &efds); maxfd = MAX(maxfd, flow->fd); prepare_wfds(&now, flow, &wfds); prepare_rfds(&now, flow, &rfds); } } return num_flows; }
int add_flow_source(struct _request_add_flow_source *request) { #ifdef TCP_CONGESTION socklen_t opt_len = 0; #endif /* TCP_CONGESTION */ struct _flow *flow; if (num_flows >= MAX_FLOWS) { logging_log(LOG_WARNING, "Can not accept another flow, already handling MAX_FLOW flows."); request_error(&request->r, "Can not accept another flow, already handling MAX_FLOW flows."); return -1; } flow = &flows[num_flows++]; init_flow(flow, 1); flow->settings = request->settings; flow->source_settings = request->source_settings; /* be greedy with buffer sizes */ flow->write_block = calloc(1, flow->settings.maximum_block_size); flow->read_block = calloc(1, flow->settings.maximum_block_size); if (flow->write_block == NULL || flow->read_block == NULL) { logging_log(LOG_ALERT, "could not allocate memory for read/write blocks"); request_error(&request->r, "could not allocate memory for read/write blocks"); uninit_flow(flow); num_flows--; return -1; } if (flow->settings.byte_counting) { int byte_idx; for (byte_idx = 0; byte_idx < flow->settings.maximum_block_size; byte_idx++) *(flow->write_block + byte_idx) = (unsigned char)(byte_idx & 0xff); } flow->state = GRIND_WAIT_CONNECT; flow->fd = name2socket(flow, flow->source_settings.destination_host, flow->source_settings.destination_port, &flow->addr, &flow->addr_len, 0, flow->settings.requested_read_buffer_size, &request->real_read_buffer_size, flow->settings.requested_send_buffer_size, &request->real_send_buffer_size); if (flow->fd == -1) { logging_log(LOG_ALERT, "Could not create data socket: %s", flow->error); request_error(&request->r, "Could not create data socket: %s", flow->error); uninit_flow(flow); num_flows--; return -1; } if (set_flow_tcp_options(flow) == -1) { request->r.error = flow->error; flow->error = NULL; uninit_flow(flow); num_flows--; return -1; } #ifdef TCP_CONGESTION opt_len = sizeof(request->cc_alg); if (getsockopt(flow->fd, IPPROTO_TCP, TCP_CONGESTION, request->cc_alg, &opt_len) == -1) { request_error(&request->r, "failed to determine actual congestion control algorithm: %s", strerror(errno)); uninit_flow(flow); num_flows--; return -1; } #endif /* TCP_CONGESTION */ #ifdef HAVE_LIBPCAP fg_pcap_go(flow); #endif /* HAVE_LIBPCAP */ if (!flow->source_settings.late_connect) { DEBUG_MSG(4, "(early) connecting test socket"); connect(flow->fd, flow->addr, flow->addr_len); flow->connect_called = 1; flow->pmtu = get_pmtu(flow->fd); } request->flow_id = flow->id; return 0; }