int neighbor_start_inactive_timer(struct ospfd *ospfd, struct interface_data *i, struct neighbor *neighbor) { int ret; struct inactivity_timer_data *inactivity_timer_data; /* FIXME: hardcoded time-out */ struct timespec timespec = { 10, 0 }; struct ev_entry *ev_entry; msg(ospfd, DEBUG, "prepare inactive timer for neighbor"); inactivity_timer_data = xzalloc(sizeof(struct inactivity_timer_data)); inactivity_timer_data->ospfd = ospfd; inactivity_timer_data->neighbor = neighbor; inactivity_timer_data->interface_data = i; neighbor->inactivity_timer_data = inactivity_timer_data; ev_entry = ev_timer_new(×pec, neighbor_inactive_timer_expired, inactivity_timer_data); if (!ev_entry) { err_msg_die(EXIT_FAILURE, "Cannot initialize a new timer"); } inactivity_timer_data->neighbor->inactive_timer_entry = ev_entry; ret = ev_add(ospfd->ev, ev_entry); if (ret != EV_SUCCESS) { err_msg_die(EXIT_FAILURE, "Cannot add new timer to global event handler"); } return SUCCESS; }
int main(int ac, char **av) { int ret; struct ospfd *ospfd; int ev_flags = 0; ospfd = alloc_ospfd(); ret = parse_cli_options(ospfd, ac, av); if (ret != SUCCESS) err_msg_die(EXIT_FAILURE, "Can't parse command line"); msg(ospfd, GENTLE, PROGRAMNAME " - " VERSIONSTRING); /* seed pseudo number randon generator */ init_pnrg(ospfd); /* initialize event subsystem. In this case this belongs * to open a epoll filedescriptor */ ospfd->ev = ev_new(); if (!ospfd->ev) err_msg_die(EXIT_FAILURE, "Can't initialize event subsystem"); ret = parse_rc_file(ospfd); if (ret != SUCCESS) err_msg_die(EXIT_FAILURE, "Can't parse configuration file"); ret = init_network(ospfd); if (ret != SUCCESS) err_msg_die(EXIT_FAILURE, "Can't initialize network subsystem"); /* and branch into the main loop * This loop will never return (with the exception of SIGINT or failure * condition) */ ret = ev_loop(ospfd->ev, ev_flags); if (ret != SUCCESS) err_msg_die(EXIT_FAILURE, "Main loop returned unexpected - exiting now"); fini_network(ospfd); free_options(ospfd); ev_free(ospfd->ev); free_ospfd(ospfd); return EXIT_SUCCESS; }
static void dump_tcp_opt(struct opts *optsp) { switch (optsp->workmode) { case MODE_TRANSMIT: fprintf(stdout, "# workmode: transmit\n"); fprintf(stdout, "# destination host: %s\n", optsp->hostname); fprintf(stdout, "# filename: %s\n", optsp->infile); break; case MODE_RECEIVE: fprintf(stdout, "# workmode: receive\n"); break; default: err_msg_die(EXIT_FAILMISC, "Programmed Failure"); break; }; if (optsp->perform_rtt_probe) { fprintf(stdout, "# perform rtt probe: true\n"); } else { fprintf(stdout, "# perform rtt probe: false\n"); } }
/* probe_rtt read a rtt probe packet, set nse_nxt_hdr to zero, ** nse_len to the current packet size and send it back to origin */ static int process_rtt_probe(int peer_fd, uint16_t nse_len) { int ret = 0; uint16_t *intptr; struct ns_rtt_probe *ns_rtt_probe_ptr; char buf[nse_len * 4 + 4]; ssize_t to_read = nse_len * 4; if (readn(peer_fd, buf + 4, to_read) != to_read) return -1; ns_rtt_probe_ptr = (struct ns_rtt_probe *) buf; msg(STRESSFUL, "process rtt probe (sequence: %d, type: %d packet_size: %d)", ntohs(ns_rtt_probe_ptr->seq_no), ntohs(ns_rtt_probe_ptr->type), to_read); ns_rtt_probe_ptr->type = htons(RTT_REPLY_TYPE); intptr = (uint16_t *)buf; *intptr = 0; intptr = (uint16_t *)buf + sizeof(uint16_t); *intptr = htons(nse_len); if (writen(peer_fd, buf, to_read + 4) != to_read + 4) err_msg_die(EXIT_FAILHEADER, "Can't reply to rtt probe!\n"); return ret; }
/* get_sock_opts() appoint some socket specific ** values for further use ... (hopefully ;-) ** Values are determined by hand for the possibility ** to change something ** We should call this function after socket creation ** and at the and off our transmit/receive phase ** --HGN */ int get_sock_opts(int fd, struct net_stat *ns) { int ret; if (opts.family != AF_INET && opts.family != AF_INET6) return 0; ret = get_ip_sock_opts(fd, ns); if (ret != 0) { return ret; } switch (opts.protocol) { case IPPROTO_TCP: return get_tcp_sock_opts(fd, ns); break; case IPPROTO_UDP: case IPPROTO_UDPLITE: case IPPROTO_DCCP: case IPPROTO_SCTP: return 0; default: err_msg_die(EXIT_FAILMISC, "Programmed Failure"); } return 0; }
static int parse_sctp_opt(int ac, char *av[],struct opts *optsp) { /* memorize protocol */ optsp->ns_proto = NS_PROTO_SCTP; optsp->perform_rtt_probe = 1; optsp->protocol = IPPROTO_SCTP; optsp->socktype = SOCK_STREAM; /* this do/while loop parse options in the form '-x'. * After the do/while loop the parse fork into transmit, * receive specific code. */ for (;;) { #define FIRST_ARG_INDEX 0 /* break if we reach the end of the OPTIONS */ if (!av[FIRST_ARG_INDEX] || av[FIRST_ARG_INDEX][0] != '-') break; if (!av[FIRST_ARG_INDEX][1] || !isalnum(av[FIRST_ARG_INDEX][1])) print_usage(NULL, HELP_STR_TCP, 1); } #undef FIRST_ARG_INDEX switch (optsp->workmode) { case MODE_TRANSMIT: /* sanity check first */ if (ac <= 1) print_usage("sctp transmit mode requires file and destination address\n", HELP_STR_SCTP, 1); optsp->infile = xstrdup(av[0]); optsp->hostname = xstrdup(av[1]); break; case MODE_RECEIVE: switch (ac) { case 0: /* nothing to do */ break; case 2: opts.hostname = xstrdup(av[1]); /* fallthrough */ case 1: opts.outfile = xstrdup(av[0]); break; default: err_msg("You specify to many arguments!"); print_usage(NULL, HELP_STR_GLOBAL, 1); break; }; break; default: err_msg_die(EXIT_FAILINT, "Internal, programmed error - unknown tranmit mode: %d\n", optsp->workmode); } return SUCCESS; }
static void timout_handler(int sig_no) { if (sig_no != SIGALRM) err_msg_die(EXIT_FAILINT, "Programmed error (received an unknow signal)"); alarm(TIMEOUT_SEC); msg(STRESSFUL, "timout occure while wait for rtt probe response"); }
int process_state_init_to_new_state(struct ospfd *o, struct interface_data *i, struct neighbor *n, int s) { switch (s) { case NEIGHBOR_EV_HELLO_RECEIVED: return process_state_init_ev_hello_received(o, i, n, s); break; default: err_msg_die(EXIT_FAILURE, "State no handled: %s:%d", __FILE__, __LINE__); break; } /* should not happened - to shut-up gcc warnings */ return FAILURE; }
int neighbor_process_event(struct ospfd *ospfd, struct interface_data *interface_data, struct neighbor *neighbor, int event) { switch (neighbor->state) { case NEIGHBOR_STATE_DOWN: return process_state_down_to_new_state(ospfd, interface_data, neighbor, event); break; case NEIGHBOR_STATE_INIT: return process_state_init_to_new_state(ospfd, interface_data, neighbor, event); break; case NEIGHBOR_STATE_ATTEMPT: case NEIGHBOR_STATE_TWO_WAY: case NEIGHBOR_STATE_EX_START: case NEIGHBOR_STATE_EXCHANGE: case NEIGHBOR_STATE_LOADING: case NEIGHBOR_STATE_FULL: default: err_msg_die(EXIT_FAILURE, "Programmed error in switch/case statement: " "state (%d) not known or not handled", neighbor->state); break; }; return FAILURE; }
/* parse_tcp_opt set all tcp default values * within optsp and parse all tcp related options * ac is the number of arguments from MODE and av is * the correspond pointer into the array vector. We parse all tcp * related options first and within the switch/case statement we handle * transmit | receive specific options. */ static int parse_tcp_opt(int ac, char *av[], struct opts *optsp) { /* memorize protocol */ optsp->ns_proto = NS_PROTO_TCP; /* tcp has some default values too, set them here */ optsp->perform_rtt_probe = 1; optsp->protocol = IPPROTO_TCP; optsp->socktype = SOCK_STREAM; while (av[0] && av[0][0] == '-') { if (av[0][1] == 'C') optsp->tcp_use_md5sig = true; if (optsp->workmode == MODE_RECEIVE) { /* need peer ip address */ if (!av[1] || av[1][0] == '-') err_msg_die(EXIT_FAILOPT, "Option -C needs an argument (Peer IP Address)"); optsp->tcp_md5sig_peeraddr = av[1]; ac--; av++; } ac--; av++; } if (optsp->tcp_use_md5sig) msg(GENTLE, "Enabled TCP_MD5SIG option"); /* Now parse all transmit | receive specific code, plus the most * important options: the file- and hostname */ switch (optsp->workmode) { case MODE_TRANSMIT: /* sanity check first */ if (ac <= 1) print_usage("tcp transmit mode required file and destination address\n", HELP_STR_GLOBAL, 1); optsp->infile = xstrdup(av[0]); optsp->hostname = xstrdup(av[1]); break; case MODE_RECEIVE: switch (ac) { case 0: /* nothing to do */ break; case 2: opts.hostname = xstrdup(av[1]); /* fallthrough */ case 1: opts.outfile = xstrdup(av[0]); break; default: err_msg("You specify to many arguments!"); print_usage(NULL, HELP_STR_GLOBAL, 1); break; }; break; default: err_msg_die(EXIT_FAILINT, "Internal, programmed error - unknown tranmit mode: %d\n", optsp->workmode); } return SUCCESS; }
void process_state_full_to_new_state(struct ospfd *ospfd, struct neighbor *neighbor, int new_state) { err_msg_die(EXIT_FAILURE, "State handling not yet implemented: %s:%d", __FILE__, __LINE__); }
/* This is the plan: ** send n rtt packets into the wire and wait until all n reply packets ** arrived. If a timeout occur we count this packet as lost */ static int probe_rtt(int peer_fd, int next_hdr, int probe_no, uint16_t backing_data_size) { int i, j, current_next_hdr; double rtt_ms[probe_no + 1], deviation = 0, covariance = 0; double d_tmp = 0; uint16_t packet_len; ssize_t to_write; char rtt_buf[backing_data_size + sizeof(struct ns_rtt_probe)]; struct ns_rtt_probe *ns_rtt_probe = (struct ns_rtt_probe *) rtt_buf; char *data_ptr = rtt_buf + sizeof(struct ns_rtt_probe); if (probe_no <= 0) err_msg_die(EXIT_FAILINT, "Programmed Failure"); memset(ns_rtt_probe, 0, sizeof(struct ns_rtt_probe)); memset(data_ptr, 'A', backing_data_size); /* packet backing data MUST a multiple of four */ packet_len = ((uint16_t)(backing_data_size / 4)) * 4; to_write = packet_len + sizeof(struct ns_rtt_probe); /* we announce packetsize in 4 byte slices (32bit) ** minus nse_nxt_hdr and nse_len header (4 byte) */ ns_rtt_probe->nse_len = htons((to_write - 4) / 4); ns_rtt_probe->ident = htons(getpid() & 0xffff); current_next_hdr = NSE_NXT_RTT_PROBE; /* FIXME: ** The first rtt probe packet had nearly a rtt of additional ** 200ms. So we ignore the first packet silently and calculate ** rtt and variation. I think this increased measurement is caused ** by "cold code paths"[TM], but these is to validate. */ for (i = 0; i <= probe_no; ) { char reply_buf[to_write]; struct ns_rtt_probe *ns_rtt_reply; ssize_t to_read = to_write; struct timeval tv, tv_tmp, tv_res; if (i++ >= probe_no) current_next_hdr = next_hdr; ns_rtt_probe->nse_nxt_hdr = htons(current_next_hdr); ns_rtt_probe->type = (htons((uint16_t)RTT_REQUEST_TYPE)); ns_rtt_probe->seq_no = htons(i); /* set timeval for packet */ if (gettimeofday(&tv, NULL) != 0) err_sys("Can't call gettimeofday"); ns_rtt_probe->sec = htonl(tv.tv_sec); ns_rtt_probe->usec = htonl(tv.tv_usec); /* transmitt rtt probe ... */ if (writen(peer_fd, ns_rtt_probe, to_write) != to_write) err_msg_die(EXIT_FAILHEADER, "Can't send rtt extension header!\n"); /* ... and receive probe */ if (readn(peer_fd, reply_buf, to_read) != to_read) return -1; if (gettimeofday(&tv, NULL) != 0) err_sys("Can't call gettimeofday"); ns_rtt_reply = (struct ns_rtt_probe *) reply_buf; tv_tmp.tv_sec = ntohl(ns_rtt_reply->sec); tv_tmp.tv_usec = ntohl(ns_rtt_reply->usec); subtime(&tv_tmp, &tv, &tv_res); /* sanity check (ident) */ if (ntohs(ns_rtt_reply->ident) != (getpid() & 0xffff)) err_msg("received a unknown rtt probe reply (ident should: %d is: %d)", ntohs(ns_rtt_reply->ident), (getpid() & 0xffff)); /* XXX: if you change something here, then check all counter ** variables, etc */ if (i == 1) continue; rtt_ms[i - 2] = (tv_res.tv_sec * 1000) + ((double)tv_res.tv_usec / 1000); msg(STRESSFUL, "receive rtt reply probe (sequence: %d, len %d, rtt: %.3fms)", ntohs(ns_rtt_reply->seq_no), to_read, rtt_ms[i - 2]); } /* average */ for (j = 0; j < probe_no; j++) net_stat.rtt_probe.usec += rtt_ms[j]; net_stat.rtt_probe.usec /= --j; /* ... covariance and standard deviation */ for (j = 0; j < probe_no; j++) covariance += pow(rtt_ms[j] - net_stat.rtt_probe.usec, 2); covariance /= --j; deviation = sqrt(covariance); d_tmp = 0; /* low and high pass deviation based filter, calculates new rtt average */ for (j = 0, i = 0; j < probe_no; j++) { if (((rtt_ms[j] > net_stat.rtt_probe.usec) && (rtt_ms[j] < (net_stat.rtt_probe.usec + (deviation * opts.rtt_probe_opt.deviation_filter)))) || ((rtt_ms[j] < net_stat.rtt_probe.usec) && (rtt_ms[j] > (net_stat.rtt_probe.usec - (deviation * opts.rtt_probe_opt.deviation_filter))))) { d_tmp += rtt_ms[j]; ++i; } } net_stat.rtt_probe.usec = d_tmp / i; msg(LOUDISH, "average rtt: %.3fms (after filter), covariance: %.3fms^2, standard deviation %.3fms", net_stat.rtt_probe.usec, covariance, deviation); return 0; }
/* return -1 if a failure occure, zero apart from that */ int meta_exchange_rcv(int peer_fd, struct peer_header_info **hi) { int ret; int invalid_ext_seen = 0; uint16_t extension_type, extension_size; struct peer_header_info *phi; unsigned char *ptr; ssize_t rc = 0, to_read = sizeof(struct ns_hdr); struct ns_hdr ns_hdr; memset(&ns_hdr, 0, sizeof(struct ns_hdr)); /* allocate info header */ phi = xzalloc(sizeof(struct peer_header_info)); *hi = phi; ptr = (unsigned char *) &ns_hdr; msg(STRESSFUL, "fetch general header (%d byte)", sizeof(struct ns_hdr)); /* read minimal ns header */ if (readn(peer_fd, &ptr[rc], to_read) != to_read) return -1; /* ns header is in -> sanity checks and look if peer specified extension header */ if (ntohs(ns_hdr.magic) != NS_MAGIC) { err_msg_die(EXIT_FAILHEADER, "received an corrupted header" "(should %d but is %d)!\n", NS_MAGIC, ntohs(ns_hdr.magic)); } msg(STRESSFUL, "header info (magic: %d, version: %d, data_size: %d)", ntohs(ns_hdr.magic), ntohs(ns_hdr.version), ntohl(ns_hdr.data_size)); phi->data_size = ntohl(ns_hdr.data_size); extension_type = ntohs(ns_hdr.nse_nxt_hdr); if (extension_type == NSE_NXT_DATA) { msg(STRESSFUL, "end of extension header processing (NSE_NXT_DATA, no extension header)"); return 0; } while (invalid_ext_seen < INVALID_EXT_TRESH_NO) { /* FIXME: define some header size macros */ uint16_t common_ext_head[2]; to_read = sizeof(uint16_t) * 2; /* read first 4 octets of extension header, because we now ** there IS a extension header and a extension header is always ** 4 byte */ if (readn(peer_fd, common_ext_head, to_read) != to_read) return -1; extension_size = ntohs(common_ext_head[1]); switch (extension_type) { case NSE_NXT_DATA: msg(STRESSFUL, "end of extension header processing (NSE_NXT_DATA)"); return 0; case NSE_NXT_NONXT: msg(STRESSFUL, "end of extension header processing (NSE_NXT_NONXT)"); return process_nonxt(peer_fd, extension_size); break; case NSE_NXT_DIGEST: msg(STRESSFUL, "next extension header: %s", "NSE_NXT_DIGEST"); err_msg("Not implementet yet: NSE_NXT_DIGEST\n"); break; case NSE_NXT_RTT_PROBE: msg(STRESSFUL, "next extension header: %s", "NSE_NXT_RTT_PROBE"); ret = process_rtt_probe(peer_fd, extension_size); if (ret == -1) return -1; break; case NSE_NXT_RTT_INFO: msg(STRESSFUL, "next extension header: %s", "NSE_NXT_RTT_INFO"); ret = process_rtt_info(peer_fd, extension_size); if (ret == -1) return -1; break; default: ++invalid_ext_seen; err_msg("received an unknown extension type (%d)!\n", extension_type); ret = process_nonxt(peer_fd, extension_size); if (ret == -1) return -1; break; } extension_type = ntohs(common_ext_head[0]); switch (extension_type) { case NSE_NXT_DATA: msg(STRESSFUL, "end of extension header processing (NSE_NXT_DATA)"); return 0; case NSE_NXT_NONXT: msg(STRESSFUL, "end of extension header processing (NSE_NXT_NONXT)"); return process_nonxt(peer_fd, extension_size); break; } }; /* failure if we reach here (failure in previous while loop */ return -1; }
int meta_exchange_snd(int connected_fd, int file_fd) { int ret = 0; ssize_t len; ssize_t file_size; struct ns_hdr ns_hdr; struct stat stat_buf; int perform_rtt; memset(&ns_hdr, 0, sizeof(struct ns_hdr)); /* fetch file size */ xfstat(file_fd, &stat_buf, opts.infile); file_size = S_ISREG(stat_buf.st_mode) ? stat_buf.st_size : 0; ns_hdr.magic = htons(NS_MAGIC); /* FIXME: catch overflow */ ns_hdr.version = htons((uint16_t) strtol(VERSIONSTRING, (char **)NULL, 10)); ns_hdr.data_size = htonl(file_size); perform_rtt = (opts.rtt_probe_opt.iterations > 0) ? 1 : 0; ns_hdr.nse_nxt_hdr = perform_rtt ? htons(NSE_NXT_RTT_PROBE) : htons(NSE_NXT_DATA); len = sizeof(struct ns_hdr); if (writen(connected_fd, &ns_hdr, len) != len) err_msg_die(EXIT_FAILHEADER, "Can't send netsend header!\n"); /* probe for effective round trip time */ if (opts.rtt_probe_opt.iterations > 0) { int flag_old; struct sigaction sa; /* initialize signalhandler for timeout handling */ sigemptyset(&sa.sa_mask); sa.sa_flags = SA_INTERRUPT; /* don't restart system calls */ sa.sa_handler = timout_handler; if (sigaction(SIGALRM, &sa, NULL) != 0) err_sys("Can't add signal handler"); /* set TCP_NODELAY so tcp writes dont get buffered */ if (opts.protocol == IPPROTO_TCP) { if ((flag_old = set_nodelay(connected_fd, 1)) < 0) { err_sys("Can't set TCP_NODELAY for socket (ret: %d)", flag_old); } } alarm(TIMEOUT_SEC); probe_rtt(connected_fd, NSE_NXT_DATA, opts.rtt_probe_opt.iterations, opts.rtt_probe_opt.data_size); alarm(0); /* and restore TCP_NOPUSH */ if (opts.protocol == IPPROTO_TCP) { if ((set_nodelay(connected_fd, flag_old)) < 0) { err_sys("Can't set TCP_NODELAY for socket"); } } /* transmitt our rtt probe results to our peer */ send_rtt_info(connected_fd, NSE_NXT_DATA, &net_stat.rtt_probe); } /* XXX: add shasum next header if opts.sha, modify nse_nxt_hdr processing */ return ret; }
int open_input_file(void) { int fd, ret; struct stat stat_buf; if (!strncmp(opts.infile, "-", 1)) { return STDIN_FILENO; } /* We don't want to read from a regular file ** rather we want to execute a program and take ** this output as our data source. */ if (opts.execstring) { pid_t pid; int pipefd[2]; xpipe(pipefd); switch (pid = fork()) { case -1: err_sys_die(EXIT_FAILMISC, "Can't fork"); case 0: close(STDOUT_FILENO); close(STDERR_FILENO); close(pipefd[0]); dup(pipefd[1]); dup(pipefd[1]); system(opts.execstring); exit(0); break; default: close(pipefd[1]); return pipefd[0]; break; } } /* Thats the normal case: we open a regular file and take ** the content as our source. */ ret = stat(opts.infile, &stat_buf); if (ret == -1) err_sys_die(EXIT_FAILMISC, "Can't stat file %s", opts.infile); #if 0 if (!(stat_buf.st_mode & S_IFREG)) { err_sys("Not an regular file %s", opts.infile); exit(EXIT_FAILOPT); } #endif #ifdef O_NOATIME fd = open(opts.infile, O_RDONLY|O_NOATIME); #else fd = open(opts.infile, O_RDONLY); #endif if (fd == -1) err_msg_die(EXIT_FAILMISC, "Can't open input file: %s", opts.infile); return fd; }
/* * performs all socketopts specified, except * for some highly protocol dependant options (e.g. TCP_MD5SIG). */ void set_socketopts(int fd) { int i, ret; const void *optval; socklen_t optlen; /* loop over all selectable socket options */ for (i = 0; socket_options[i].sockopt_name; i++) { if (!socket_options[i].user_issue) continue; /* * this switch statement checks that the particular * socket option matches our selected socket-type */ switch (socket_options[i].level) { case SOL_SOCKET: break; /* works on every socket */ /* fall-through begins here ... */ case IPPROTO_IP: case IPPROTO_IPV6: case IPPROTO_TCP: if (opts.protocol == IPPROTO_TCP) break; case IPPROTO_UDP: if (opts.protocol == IPPROTO_UDP) break; case IPPROTO_UDPLITE: if (opts.protocol == IPPROTO_UDPLITE) break; case IPPROTO_SCTP: if (opts.protocol == IPPROTO_SCTP) break; case SOL_DCCP: if (opts.protocol == IPPROTO_DCCP) break; default: /* and exit if socketoption and sockettype did not match */ err_msg_die(EXIT_FAILMISC, "You selected an socket option which isn't " "compatible with this particular socket option"); } /* ... and do the dirty: set the socket options */ switch (socket_options[i].sockopt_type) { case SVT_BOOL: case SVT_INT: case SVT_TOINT: optlen = sizeof(socket_options[i].value); optval = &socket_options[i].value; break; case SVT_TIMEVAL: optlen = sizeof(socket_options[i].tv); optval = &socket_options[i].tv; break; case SVT_STR: optlen = strlen(socket_options[i].value_ptr) + 1; optval = socket_options[i].value_ptr; break; default: err_msg_die(EXIT_FAILNET, "Unknown sockopt_type %d\n", socket_options[i].sockopt_type); } ret = setsockopt(fd, socket_options[i].level, socket_options[i].option, optval, optlen); if (ret) err_sys_die(EXIT_FAILMISC, "setsockopt option %d (name %s) failed", socket_options[i].sockopt_type, socket_options[i].sockopt_name); } }