/* $Id: telnet.c,v 10.1 92/10/06 23:07:14 ca Exp $ */ /* * MaRS Maryland Routing Simulator * Copyright (c) 1991 University of Maryland * All Rights Reserved. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of U.M. not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. U.M. makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M. * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Authors: Cengiz Alaettinoglu, Klaudia Dussa-Zieger, Ibrahim Matta * Systems Design and Analysis Group * Department of Computer Science * University of Maryland at College Park. */ /* * Simulate Application/Transport module with telnet traffic pattern * */ #include #include #include #include "sim.h" #include "q.h" #include "list.h" #include "component.h" #include "log.h" #include "comptypes.h" #include "packet.h" #include "eventdefs.h" #include "event.h" #include "telnet.h" #include "perf.h" #ifdef DEBUG extern Log debug_log; #endif static caddr_t instant_rate(); /* imm-- function to compute the instantaneous thruput */ static caddr_t telnet_source_create(), telnet_sink_create(), telnet_neighbor(), telnet_uneighbor(), telnet_source_start(), telnet_sink_start(), telnet_reset(), telnet_produce(), telnet_consume(), telnet_retransmit(), telnet_send(), telnet_receive(), telnet_mk_peer(), telnet_conn_on(), telnet_conn_down(); #define ticks_btw_conns(g) (random_no(g->dist_chosen->u.i, \ (double)g->telnet_tr_delay->u.i, \ g->sd_conn->u.d, \ (unsigned) 1, \ (unsigned) 10000000) \ * 1000 / USECS_PER_TICK) #define car_length(g) (pval(g, telnet_car_length)->u.i) #define ticks_btw_packets(g) (random_no(g->dist_chosen->u.i, \ (double)g->telnet_car_delay->u.i, \ g->sd_packet->u.d, \ (unsigned) 1, \ (unsigned) 1000000) / USECS_PER_TICK) #define ticks_to_response(g) (random_no(g->dist_chosen->u.i, \ (double)g->telnet_response_delay->u.i, \ g->sd_response->u.d, \ (unsigned) 10, \ (unsigned) 1000000) / USECS_PER_TICK) /* Geometrically distributed */ #define response_length(g) random_no(2, \ (double)g->telnet_response_length->u.i, \ 0.0, \ (unsigned) 10, \ (unsigned) 100000) static int train_length(g) register Telnett *g; { int length; if (g->telnet_car_per_tr->u.i == -1) length = -1; else /* Geometrically distributed */ length = random_no(2, (double) pval(g, telnet_car_per_tr)->u.i, (double) 0.0, (unsigned) 1, (unsigned) 99999999); return length; } caddr_t telnet_source_action(src, g, type, pkt, arg) Component *src; register Telnett *g; int type; Packet *pkt; caddr_t arg; { caddr_t result = NULL; dbg_set_level(DBG_ERR); /* Just a big switch on type of event */ switch (type) { case EV_RESET : #ifdef DEBUG dbg_write(debug_log, DBG_INFO, (Component *)g, "reset"); #endif result = telnet_reset(g); break; case EV_CREATE: /* Minor sanity check first-- g should be NULL when initializing. */ #ifdef DEBUG if (g) dbg_write(debug_log, DBG_INFO, (Component *)NULL, "TELNET Generator initialization called with non-null pointer."); #endif result = telnet_source_create((char *)arg); break; case EV_DEL: free(g->telnet_peer->u.p); comp_delete((Component *)g); result = (caddr_t)g; break; case EV_NEIGHBOR: result = telnet_neighbor(g, (Component *)arg); break; case EV_UNEIGHBOR: result = telnet_uneighbor(g, (Component *)arg); break; case EV_MK_PEER: result = telnet_mk_peer(g, (Component *)arg, TELNET_SINK); break; case EV_START: result = telnet_source_start(g); break; case EV_STOP: result = (caddr_t) g; break; /********** The preceding were the commands. Following are the actual events that the application/transport module expects to receive. */ case EV_APTR_CONN_ON: result = telnet_conn_on((Component *)src, g, (int)arg); break; case EV_APTR_PRODUCE: result = telnet_produce(g, arg); break; case EV_APTR_CONSUME: result = telnet_consume(g, pkt); break; case EV_APTR_SEND: result = telnet_send(g, arg); break; case EV_APTR_RECEIVE: result = telnet_receive(g, src, pkt); break; case EV_APTR_RETRANSMIT: result = telnet_retransmit(g, pkt); break; /* imm -- */ case EV_INSTANT_RATE: result = instant_rate(g); break; default: #ifdef DEBUG dbg_write(debug_log, DBG_ERR, (Component *)g, "got unexpected event of type %x", type); #endif break; } return(result); } /****************************************/ static caddr_t telnet_source_create(name) register char *name; { Telnett *newg; /* Memory for the component structure. */ newg = (Telnett *)sim_malloc(sizeof(Telnett)); /* First things first-- copy name into the new structure. */ strncpy(newg->telnet_name, name, 40); /* have to create a neighbor list */ newg->telnet_neighbors = l_create(); newg->telnet_params = q_create(); newg->telnet_class = APTR_CLASS; newg->telnet_type = TELNET_SOURCE; newg->telnet_action = telnet_source_action; newg->telnet_menu_up = FALSE; newg->source_socket.so_port = (Component *) newg; newg->source_socket.so_host = (Component *) NULL; newg->dest_socket.so_port = (Component *) NULL; newg->dest_socket.so_host = (Component *) NULL; /* Initialize the parameters */ (void)param_init((Component *)newg, "Name", (PFD)NULL, make_name_text, make_short_name_text, param_input_name, 0, DisplayMask | InputMask, 0.0); newg->telnet_peer = param_init((Component *)newg, "Peer", (PFD)NULL, make_str_text, make_short_str_text, (PFI)NULL, 0, DisplayMask, 0.0); pval(newg, telnet_peer)->u.p = sim_malloc(40); strcpy(newg->telnet_peer->u.p, "unknown"); newg->telnet_select = param_init((Component *)newg, "Not select(0), select(1) ", (PFD)NULL, make_int_text, make_short_int_text, param_input_int, 0, DisplayMask | ModifyMask, 0.0); newg->telnet_select->u.i = 0; newg->init_conn_on_off = param_init((Component *)newg, "Init Connection status", (PFD)NULL, make_str_text, make_short_str_text, param_input_str, TIME_HISTORY, DisplayMask | ModifyMask, 0.0); newg->init_conn_on_off->u.p = (caddr_t) sim_malloc(40); strcpy(newg->init_conn_on_off->u.p, "Off"); newg->conn_on_off = param_init((Component *)newg, "Connection status", (PFD)NULL, make_str_text, make_short_str_text, (PFI)NULL, TIME_HISTORY, DisplayMask | CanHaveLogMask, 0.0); pval(newg, conn_on_off)->u.p = "Off"; newg->telnet_tr_how_many = param_init((Component *)newg, "How many connections (-1 inf)", (PFD)NULL, make_int_text, make_short_int_text, param_input_int, 0, DisplayMask | ModifyMask, 0.0); newg->telnet_tr_how_many->u.i = -1; newg->telnet_car_per_tr = param_init((Component *)newg, "Ave no of packets per conn.", (PFD)NULL, make_int_text, make_short_int_text, param_input_int, 0, DisplayMask | ModifyMask, 0.0); newg->telnet_car_per_tr->u.i = -1; newg->telnet_car_length = param_init((Component *)newg, "Ave Packet length", (PFD)NULL, make_int_text, make_short_int_text, param_input_int, 0, DisplayMask | ModifyMask, 0.0); newg->telnet_car_length->u.i = 128; newg->telnet_response_length = param_init((Component *)newg, "Ave Response Packet length", (PFD)NULL, make_int_text, make_short_int_text, param_input_int, 0, 0, 0.0); newg->telnet_response_length->u.i = 512; newg->telnet_tr_delay = param_init((Component *)newg, "Ave delay btw conns (mSec)", (PFD)NULL, make_int_text, make_short_int_text, param_input_int, 0, DisplayMask | ModifyMask, 0.0); newg->telnet_tr_delay->u.i = 60000; newg->telnet_car_delay = param_init((Component *)newg, "Ave delay btw packets (uSec)", (PFD)NULL, make_int_text, make_short_int_text, param_input_int, 0, DisplayMask | ModifyMask, 0.0); newg->telnet_car_delay->u.i = 1000000; newg->telnet_response_delay = param_init((Component *)newg, "Ave Delay to Response (uSec)", (PFD)NULL, make_int_text, make_short_int_text, param_input_int, 0, 0, 0.0); newg->telnet_response_delay->u.i = 1000000; newg->dist_chosen = param_init((Component *)newg, "Choose dist (0=>EXP, 1=>UNIF)", (PFD)NULL, make_int_text, make_short_int_text, param_input_int, 0, DisplayMask | ModifyMask, 0.0); pval(newg, dist_chosen)->u.i = 0; newg->sd_conn = param_init((Component *)newg, "Std deviation for conn. if UNIF", (PFD)NULL, make_double_text, make_short_double_text, param_input_double, 0, DisplayMask | ModifyMask, 0.0); pval(newg, sd_conn)->u.d = 0.0; newg->sd_packet = param_init((Component *)newg, "Std deviation for packets if UNIF", (PFD)NULL, make_double_text, make_short_double_text, param_input_double, 0, DisplayMask | ModifyMask, 0.0); pval(newg, sd_packet)->u.d = 0.0; newg->sd_response = param_init((Component *)newg, "Std deviation for responses if UNIF", (PFD)NULL, make_double_text, make_short_double_text, param_input_double, 0, 0, 0.0); pval(newg, sd_response)->u.d = 0.0; newg->telnet_current_train = param_init((Component *)newg, "Current connection", int_calc, make_int_text, make_short_int_text, (PFI)NULL, TIME_HISTORY, DisplayMask | CanHaveMeterMask | CanHaveLogMask, 0.0); pval(newg, telnet_current_train)->u.i = 0; newg->telnet_car_how_many = param_init((Component *)newg, "No of packets in this conn.", (PFD)NULL, make_int_text, make_short_int_text, param_input_int, TIME_HISTORY, DisplayMask | CanHaveMeterMask | CanHaveLogMask, 0.0); pval(newg, telnet_car_how_many)->u.i = 0; newg->telnet_current_car = param_init((Component *)newg, "Current packet in this conn.", int_calc, make_int_text, make_short_int_text, (PFI)NULL, TIME_HISTORY, DisplayMask | CanHaveMeterMask | CanHaveLogMask, 0.0); pval(newg, telnet_current_car)->u.i = 0; newg->telnet_produce_ws = param_init((Component *)newg, "Max Produce w size (-1 inf)", (PFD)NULL, make_int_text, make_short_int_text, param_input_int, 0, DisplayMask | ModifyMask, 0.0); pval(newg, telnet_produce_ws)->u.i = 20; newg->telnet_send_ws = param_init((Component *)newg, "Max Send w size (-1 inf)", (PFD)NULL, make_int_text, make_short_int_text, param_input_int, 0, DisplayMask | ModifyMask, 0.0); pval(newg, telnet_send_ws)->u.i = 30; newg->telnet_packets_produced = param_init((Component *)newg, "Packets Produced", int_calc, make_int_text, make_short_int_text, (PFI)NULL, TIME_HISTORY, DisplayMask | CanHaveMeterMask | CanHaveLogMask, 0.0); pval(newg, telnet_packets_produced)->u.i = 0; newg->telnet_packets_sent = param_init((Component *)newg, "Packets Sent", int_calc, make_int_text, make_short_int_text, (PFI)NULL, TIME_HISTORY, DisplayMask | CanHaveMeterMask | CanHaveLogMask, 0.0); pval(newg, telnet_packets_sent)->u.i = 0; newg->telnet_packets_acked = param_init((Component *)newg, "Packets Acked", int_calc, make_int_text, make_short_int_text, (PFI)NULL, TIME_HISTORY, DisplayMask | CanHaveMeterMask | CanHaveLogMask, 0.0); pval(newg, telnet_packets_acked)->u.i = 0; newg->telnet_packets_recvd = param_init((Component *)newg, "Packets Received", int_calc, make_int_text, make_short_int_text, (PFI)NULL, TIME_HISTORY, DisplayMask | CanHaveMeterMask | CanHaveLogMask, 0.0); pval(newg, telnet_packets_recvd)->u.i = 0; newg->telnet_packets_retransmitted = param_init((Component *)newg, "Packets Retransmitted", int_calc, make_int_text, make_short_int_text, (PFI)NULL, TIME_HISTORY, DisplayMask | CanHaveMeterMask | CanHaveLogMask, 0.0); pval(newg, telnet_packets_retransmitted)->u.i = 0; newg->telnet_token_time = 0; newg->telnet_rtt = param_init((Component *)newg, "Round trip time est in usec", int_calc, make_int_text, make_short_int_text, (PFI)NULL, TIME_HISTORY, DisplayMask | CanHaveMeterMask | CanHaveLogMask, 0.0); pval(newg, telnet_rtt)->u.i = 10000; /* imm -- the following parameters are used to compute the instantaneous sent and acked thruputs */ newg->sent_thruput = param_init((Component *)newg, "Inst. sent rate", double_calc, make_double_text, make_short_double_text, (PFI)NULL, TIME_HISTORY, DisplayMask | CanHaveMeterMask | CanHaveLogMask, 0.0); pval(newg, sent_thruput)->u.d = 0; newg->acked_thruput = param_init((Component *)newg, "Inst. acked rate", double_calc, make_double_text, make_short_double_text, (PFI)NULL, TIME_HISTORY, DisplayMask | CanHaveMeterMask | CanHaveLogMask, 0.0); pval(newg, acked_thruput)->u.d = 0; newg->retransmission_rate = param_init((Component *)newg, "Inst. retransmission rate", double_calc, make_double_text, make_short_double_text, (PFI)NULL, TIME_HISTORY, DisplayMask | CanHaveMeterMask | CanHaveLogMask, 0.0); pval(newg, retransmission_rate)->u.d = 0; /* Compute instantaneous delay */ newg->inst_delay = param_init((Component *)newg, "Inst delay in msecs", double_calc, make_double_text, make_short_double_text, (PFI)NULL, TIME_HISTORY, DisplayMask | CanHaveMeterMask | CanHaveLogMask, 0.0); pval(newg, inst_delay)->u.d = 0.0; newg->last_pkt_delay = 0; newg->data_acked = 0; newg->last_bytes_acked = 0; newg->telnet_bytes_sent = 0; newg->telnet_bytes_retransmitted = 0; #ifdef DEBUG dbg_write(debug_log, DBG_INFO, (Component *)newg, "telnet generator initialized"); #endif return((caddr_t)newg); } static caddr_t telnet_reset(g) Telnett *g; { pval(g, telnet_packets_produced)->u.i = 0; pval(g, telnet_packets_sent)->u.i = 0; pval(g, telnet_packets_acked)->u.i = 0; pval(g, telnet_packets_recvd)->u.i = 0; pval(g, telnet_packets_retransmitted)->u.i = 0; g->telnet_token_time = 0; pval(g, telnet_rtt)->u.i = 10000; pval(g, telnet_current_train)->u.i = 0; pval(g, telnet_current_car)->u.i = 0; pval(g, telnet_car_how_many)->u.i = 0; g->last_pkt_delay = 0; g->data_acked = 0; g->last_bytes_acked = 0; g->conn_on_off->u.p = "Off"; g->sent_thruput->u.d = 0; g->acked_thruput->u.d = 0; g->inst_delay->u.d = 0; g->total_delay = 0; g->recent_pkts_acked = 0; g->retransmission_rate->u.d = 0; g->no_of_current_conns = 0; g->no_of_total_cars = 0; log_param((Component *)g, g->conn_on_off); log_param((Component *)g, g->sent_thruput); log_param((Component *)g, g->acked_thruput); log_param((Component *)g, g->inst_delay); log_param((Component *)g, g->retransmission_rate); log_param((Component *)g, g->telnet_packets_produced); log_param((Component *)g, g->telnet_packets_sent); log_param((Component *)g, g->telnet_packets_acked); log_param((Component *)g, g->telnet_packets_recvd); log_param((Component *)g, g->telnet_packets_retransmitted); log_param((Component *)g, g->telnet_rtt); log_param((Component *)g, g->telnet_current_train); log_param((Component *)g, g->telnet_current_car); log_param((Component *)g, g->telnet_car_how_many); g->total_prev_bytes_acked = 0; g->telnet_bytes_sent = 0; g->telnet_bytes_retransmitted = 0; g->produce_scheduled = 0; return((caddr_t)g); } /****************************************/ static caddr_t telnet_neighbor(g, c) register Telnett *g; register Component *c; { caddr_t result; /* Put the passed neighbor into my neighbor list, but only if it is a legal neighbor (a node). Also can only have one neighbor. */ result = (caddr_t)add_neighbor((Component *)g, c, 1, 1, NODE_CLASS); if (result != (caddr_t)NULL) g->source_socket.so_host = c; return result; } static caddr_t telnet_mk_peer(g, c, peer_type) register Telnett *g; register Component *c; int peer_type; { caddr_t result; /* Put the passed neighbor into my neighbor list, but only if it is a legal neighbor (a node). Also can only have one neighbor. */ result = (caddr_t) NULL; if (c->co_type == peer_type) if (((Telnett *)c)->source_socket.so_host != (Component *) NULL) { g->dest_socket.so_host = ((Telnett *)c)->source_socket.so_host; g->dest_socket.so_port = ((Telnett *)c)->source_socket.so_port; strcpy(g->telnet_peer->u.p, ((Component *)((Telnett *)c)->source_socket.so_host)->co_name); strcat(g->telnet_peer->u.p, "."); strcat(g->telnet_peer->u.p, c->co_name); log_param((Component *)g, g->telnet_peer); result = (caddr_t) g; } #ifdef DEBUG else dbg_write(debug_log, DBG_ERR, (Component *)g, "Source socket in destination not yet initialized"); else dbg_write(debug_log, DBG_ERR, (Component *)g, "Incompatible peer"); #endif return result; } /****************************************/ static caddr_t telnet_uneighbor(g, c) register Telnett *g; register Component *c; { return((caddr_t)remove_neighbor((Component *)g, c)); } /****************************************/ static caddr_t telnet_source_start(g) register Telnett *g; { Packet *pkt; Component *c; tick_t time; if (g->telnet_neighbors->l_len != 1) { #ifdef DEBUG dbg_write(debug_log, DBG_ERR, (Component *)g, "can't generate packets-- no neighbors."); #endif return((caddr_t)NULL); } if (g->dest_socket.so_host == (Component *)NULL) { #ifdef DEBUG dbg_write(debug_log, DBG_ERR, (Component *)g, "can't generate packets-- no peer."); #endif return((caddr_t)NULL); } /* imm -- Schedule first computation of instantaneous thruput */ ev_enqueue(EV_INSTANT_RATE, (Component *)g, (Component *)g, (tick_t)(ev_now() + perf_update_dt_usecs/USECS_PER_TICK), g->telnet_action, (Packet *)NULL, (caddr_t)NULL); /* produce first packet */ if (strcmp(g->init_conn_on_off->u.p, "On") == 0) time = ev_now(); else time = ticks_btw_conns(g); ev_enqueue(EV_APTR_CONN_ON, (Component *)g, (Component *)g, time, g->telnet_action, (Packet *)NULL, (caddr_t)train_length(g)); /* send a token packet */ pkt = pk_alloc(); pkt->pk_length = TOKEN_PKT_SIZE; pkt->pk_type = TR_PACKET; pkt->tr_pk.tr_type = TR_TOKEN; pkt->tr_pk.response = 0; pkt->pk_source_socket.so_host = g->source_socket.so_host; pkt->pk_source_socket.so_port = g->source_socket.so_port; pkt->pk_dest_socket.so_host = g->dest_socket.so_host; pkt->pk_dest_socket.so_port = g->dest_socket.so_port; c = (Component *)(((Neighbor *)g->telnet_neighbors->l_head)->n_c); ev_enqueue(EV_NODE_PRODUCE, (Component *)g, c, ev_now(), c->co_action, pkt, (caddr_t)NULL); pm((Component *)g, TELNET_SOURCE, NEW_COMPONENT, 0, 0, 0, 0); /* Something non-NULL to return */ return((caddr_t)g); } static caddr_t telnet_conn_on(source, g, no_of_pkts) /* Start a connection i.e. train, * the no of pkts are passed as a parameter instead of calculating it here, * useful with -play command line option */ register Telnett *g; register Component *source; int no_of_pkts; { /* if -play in effect, and this event is read from a file then the source is NULL, o/w just return */ if (play_flag && source) return (caddr_t) g; g->conn_on_off->u.p = "On"; log_param((Component *)g, g->conn_on_off); pm((Component *)g, TELNET_SOURCE, CONN_UP, 0, 0, 0, g->telnet_select->u.i); g->telnet_car_how_many->u.i = no_of_pkts; if (g->no_of_current_conns) { /* did the previous conn ended ? */ #ifdef DEBUG if (g->no_of_current_conns > 1) printf("%s : %d conns overlapped %d total cars\n", g->telnet_name, g->no_of_current_conns, g->no_of_total_cars); #endif g->no_of_current_conns++; g->no_of_total_cars += g->telnet_car_how_many->u.i; } else { g->no_of_current_conns = 1; g->no_of_total_cars = g->telnet_car_how_many->u.i; g->conn_start_time = ev_now(); } g->telnet_current_train->u.i++; log_param((Component *)g, g->telnet_car_how_many); log_param((Component *)g, g->telnet_current_car); log_param((Component *)g, g->telnet_current_train); if (!g->produce_scheduled) { g->produce_scheduled = 1; /* start producing, o/w we are already producing */ ev_call(EV_APTR_PRODUCE, (Component *)g, (Component *)g, g->telnet_action, (Packet *)NULL, (caddr_t)NULL); } return (caddr_t) g; } static caddr_t telnet_conn_down(g) /* End the connection i.e. train */ register Telnett *g; { g->conn_on_off->u.p = "Off"; log_param((Component *)g, g->conn_on_off); pm((Component *)g, TELNET_SOURCE, CONN_DOWN, (int)(TICKS_TO_USECS(ev_now() - g->conn_start_time) / 1000), g->no_of_total_cars, g->no_of_current_conns, g->telnet_select->u.i); g->no_of_total_cars = 0; g->no_of_current_conns = 0; g->telnet_current_car->u.i= 0; log_param((Component *)g, g->telnet_current_car); } static caddr_t telnet_produce(g, arg) /* Produce a packet * arg = NULL means a regular produce * if not a response produce, i.e. do not schedule the next produce */ register Telnett *g; caddr_t arg; { if (arg != (caddr_t) NULL) { /* A response packet */ if (g->telnet_packets_produced->u.i - g->telnet_packets_sent->u.i < g->telnet_produce_ws->u.i || g->telnet_produce_ws->u.i == -1) { g->telnet_packets_produced->u.i ++; log_param((Component *) g, g->telnet_packets_produced); ev_call(EV_APTR_SEND, (Component *)g, (Component *)g, g->telnet_action, (Packet *)NULL, arg); } else { /* try later : window full */ ev_enqueue(EV_APTR_PRODUCE, (Component *)g, (Component *)g, ev_now() + ticks_btw_packets(g), g->telnet_action, (Packet *)NULL, arg); } return arg; } /* is there a place in the produce window */ if (g->telnet_packets_produced->u.i - g->telnet_packets_sent->u.i < g->telnet_produce_ws->u.i || g->telnet_produce_ws->u.i == -1) { g->telnet_packets_produced->u.i ++; g->telnet_current_car->u.i++; log_param((Component *)g, g->telnet_packets_produced); log_param((Component *)g, g->telnet_current_car); ev_call(EV_APTR_SEND, (Component *)g, (Component *)g, g->telnet_action, (Packet *)NULL, (caddr_t)NULL); #ifdef DEBUG dbg_write(debug_log, DBG_INFO, (Component *)g, "Produced a packet"); #endif } if (g->telnet_current_car->u.i != g->no_of_total_cars) /* schedule an event for the next pkt */ ev_enqueue(EV_APTR_PRODUCE, (Component *)g, (Component *)g, ev_now() + ticks_btw_packets(g), g->telnet_action, (Packet *)NULL, (caddr_t)NULL); else { g->produce_scheduled = 0; /* schedule an event for the next connection i.e. train */ if (g->telnet_current_train->u.i < g->telnet_tr_how_many->u.i || g->telnet_tr_how_many->u.i == -1) ev_enqueue(EV_APTR_CONN_ON, (Component *)g, (Component *)g, ev_now() + ticks_btw_conns(g), g->telnet_action, (Packet *)NULL, (caddr_t)train_length(g)); } return((caddr_t)g); } /********************************************************/ static caddr_t instant_rate(g) /* imm -- Compute instantaneous thruputs of this source (in bytes per msec)*/ register Telnett *g; { g->sent_thruput->u.d = ((double)g->telnet_bytes_sent) * 1000.0 / (double)perf_update_dt_usecs; log_param((Component *)g, g->sent_thruput); g->telnet_bytes_sent = 0; g->acked_thruput->u.d = ((double)g->data_acked - (double)g->total_prev_bytes_acked)* 1000.0 / (double) perf_update_dt_usecs; log_param((Component *)g, g->acked_thruput); g->total_prev_bytes_acked = g->data_acked; g->retransmission_rate->u.d = ((double)g->telnet_bytes_retransmitted) * 1000.0 / (double) perf_update_dt_usecs; /* rate is number of bytes retransmitted per msecond */ log_param((Component *)g, g->retransmission_rate); g->telnet_bytes_retransmitted = 0; /* Update instantaneous delay for this connection */ g->inst_delay->u.d = (g->recent_pkts_acked) ? ((double)g->total_delay * (double)USECS_PER_TICK) / ((double)(1000.0) * (double)g->recent_pkts_acked) : 0.0; log_param((Component *)g, g->inst_delay); g->total_delay = 0; g->recent_pkts_acked = 0; /* Schedule next computation */ ev_enqueue(EV_INSTANT_RATE, (Component *)g, (Component *)g, (tick_t)(ev_now() + perf_update_dt_usecs / USECS_PER_TICK), g->telnet_action, (Packet *)NULL, (caddr_t)NULL); return ((caddr_t)g); } static caddr_t telnet_send(g, arg) /* Send out a packet */ register Telnett *g; caddr_t arg; { register Component *c; register Packet *pkt; register unsigned int ticks, time_now; if (g->telnet_packets_produced->u.i > g->telnet_packets_sent->u.i && (g->telnet_packets_sent->u.i - g->telnet_packets_acked->u.i < g->telnet_send_ws->u.i || g->telnet_send_ws->u.i == -1)) { g->telnet_packets_sent->u.i ++; log_param((Component *) g, g->telnet_packets_sent); pkt = pk_alloc(); pkt->pk_sent_time = ev_now(); pkt->pk_length = ((arg == (caddr_t) NULL) ? car_length(g) : response_length(g)) + 32; pkt->pk_type = TR_PACKET; pkt->tr_pk.tr_type = TR_DATA; pkt->tr_pk.response = (arg == (caddr_t) NULL) ? 1 : 0; pkt->tr_pk.data_size= pkt->pk_length - 32; pkt->pk_source_socket.so_host = g->source_socket.so_host; pkt->pk_source_socket.so_port = g->source_socket.so_port; pkt->pk_dest_socket.so_host = g->dest_socket.so_host; pkt->pk_dest_socket.so_port = g->dest_socket.so_port; g->telnet_bytes_sent += pkt->tr_pk.data_size; c = (Component *)(((Neighbor *)g->telnet_neighbors->l_head)->n_c); ev_call(EV_NODE_PRODUCE, (Component *)g, c, c->co_action, pkt, (caddr_t)NULL); #ifdef DEBUG dbg_write(debug_log, DBG_INFO, (Component *)g, "sent a packet"); #endif } else if (g->telnet_current_car->u.i == g->no_of_total_cars && g->telnet_packets_produced->u.i == g->telnet_packets_sent->u.i && g->telnet_packets_sent->u.i == g->telnet_packets_acked->u.i && g->telnet_packets_produced->u.i == g->telnet_packets_recvd->u.i && g->telnet_type == TELNET_SOURCE) /* an ack is received for everything produced */ telnet_conn_down(g); return((caddr_t)g); } static caddr_t telnet_retransmit(g, pkt) /* retransmit a packet */ register Telnett *g; Packet *pkt; { register unsigned int time_now, ticks; register Component *c; time_now = ev_now(); g->telnet_packets_retransmitted->u.i ++; log_param((Component *) g, g->telnet_packets_retransmitted); g->telnet_bytes_retransmitted += pkt->tr_pk.data_size; c = (Component *)(((Neighbor *)g->telnet_neighbors->l_head)->n_c); ticks = g->telnet_rtt->u.i * 2 / USECS_PER_TICK; if (pkt->tr_pk.tr_type == TR_TOKEN) g->telnet_token_time = time_now + ticks; ev_enqueue(EV_NODE_PRODUCE, (Component *)g, c, time_now + ticks, c->co_action, pkt, (caddr_t)NULL); #ifdef DEBUG dbg_write(debug_log, DBG_INFO, (Component *)g, "retransmitted a packet"); #endif pm((Component *)g, g->telnet_type, PKT_RETRANSMITTED, 0, 0, 0, g->telnet_select->u.i); return((caddr_t)g); } caddr_t telnet_sink_action(src, g, type, pkt, arg) Component *src; register Telnett *g; int type; Packet *pkt; caddr_t arg; { caddr_t result = NULL; dbg_set_level(DBG_ERR); /* Just a big switch on type of event */ switch (type) { case EV_RESET: #ifdef DEBUG dbg_write(debug_log, DBG_INFO, (Component *)g, "reset"); #endif result = telnet_reset(g); break; case EV_CREATE: /* Minor sanity check first-- g should be NULL when initializing. */ #ifdef DEBUG if (g) dbg_write(debug_log, DBG_INFO, (Component *)NULL, "TELNET Generator initialization called with non-null pointer."); #endif result = telnet_sink_create((char *)arg); break; case EV_DEL: free(g->telnet_peer->u.p); comp_delete((Component *)g); result = (caddr_t)g; break; case EV_NEIGHBOR: result = telnet_neighbor(g, (Component *)arg); break; case EV_UNEIGHBOR: result = telnet_uneighbor(g, (Component *)arg); break; case EV_MK_PEER: result = telnet_mk_peer(g, (Component *)arg, TELNET_SOURCE); break; case EV_START: result = telnet_sink_start(g); break; case EV_STOP: result = (caddr_t) g; break; /********** The preceding were the commands. Following are the actual events that the application/transport module expects to receive. */ case EV_APTR_CONN_ON: result = telnet_conn_on(src, g,(int) arg); break; case EV_APTR_PRODUCE: result = telnet_produce(g, arg); break; case EV_APTR_CONSUME: result = telnet_consume(g, pkt); break; case EV_APTR_SEND: result = telnet_send(g, arg); break; case EV_APTR_RECEIVE: result = telnet_receive(g, src, pkt); break; case EV_APTR_RETRANSMIT: result = telnet_retransmit(g, pkt); break; default: #ifdef DEBUG dbg_write(debug_log, DBG_ERR, (Component *)g, "got unexpected event of type %x", type); #endif break; } return(result); } static caddr_t telnet_sink_create(name) register char *name; { Telnett *newg; /* Memory for the component structure. */ newg = (Telnett *)sim_malloc(sizeof(Telnett)); /* First things first-- copy name into the new structure. */ strncpy(newg->telnet_name, name, 40); /* have to create a neighbor list */ newg->telnet_neighbors = l_create(); newg->telnet_params = q_create(); newg->telnet_class = APTR_CLASS; newg->telnet_type = TELNET_SINK; newg->telnet_action = telnet_sink_action; newg->telnet_menu_up = FALSE; newg->source_socket.so_port = (Component *) newg; newg->source_socket.so_host = (Component *) NULL; newg->dest_socket.so_port = (Component *) NULL; newg->dest_socket.so_host = (Component *) NULL; /* Initialize the parameters */ (void)param_init((Component *)newg, "Name", (PFD)NULL, make_name_text, make_short_name_text, param_input_name, 0, DisplayMask | InputMask, 0.0); newg->telnet_peer = param_init((Component *)newg, "Peer", (PFD)NULL, make_str_text, make_short_str_text, (PFI)NULL, 0, DisplayMask, 0.0); pval(newg, telnet_peer)->u.p = sim_malloc(40); strcpy(newg->telnet_peer->u.p, "unknown"); newg->telnet_select = param_init((Component *)newg, "Not select(0), select(1) ", (PFD)NULL, make_int_text, make_short_int_text, param_input_int, 0, 0, 0.0); newg->telnet_select->u.i = 0; newg->conn_on_off = param_init((Component *)newg, "Connection on (high) or off (low)", (PFD)NULL, make_str_text, make_short_str_text, (PFI)NULL, TIME_HISTORY, 0, 0.0); pval(newg, conn_on_off)->u.p = "Off"; newg->telnet_tr_how_many = param_init((Component *)newg, "How many connections (-1 inf)", (PFD)NULL, make_int_text, make_short_int_text, param_input_int, 0, 0, 0.0); newg->telnet_tr_how_many->u.i = -1; newg->telnet_car_per_tr = param_init((Component *)newg, "Ave no of packets per conn.", (PFD)NULL, make_int_text, make_short_int_text, param_input_int, 0, 0, 0.0); newg->telnet_car_per_tr->u.i = -1; newg->telnet_car_length = param_init((Component *)newg, "Ave Packet length", (PFD)NULL, make_int_text, make_short_int_text, param_input_int, 0, 0, 0.0); newg->telnet_car_length->u.i = 128; newg->telnet_response_length = param_init((Component *)newg, "Ave Response Packet length", (PFD)NULL, make_int_text, make_short_int_text, param_input_int, 0, DisplayMask | ModifyMask, 0.0); newg->telnet_response_length->u.i = 512; newg->telnet_tr_delay = param_init((Component *)newg, "Ave delay btw conns (mSec)", (PFD)NULL, make_int_text, make_short_int_text, param_input_int, 0, 0, 0.0); newg->telnet_tr_delay->u.i = 100000; newg->telnet_car_delay = param_init((Component *)newg, "Ave delay btw packets (uSec)", (PFD)NULL, make_int_text, make_short_int_text, param_input_int, 0, 0, 0.0); newg->telnet_car_delay->u.i = 1000; newg->telnet_response_delay = param_init((Component *)newg, "Ave Delay to Response (uSec)", (PFD)NULL, make_int_text, make_short_int_text, param_input_int, 0, DisplayMask | ModifyMask, 0.0); newg->telnet_response_delay->u.i = 1000000; newg->dist_chosen = param_init((Component *)newg, "Choose dist (0=>EXP, 1=>UNIF)", (PFD)NULL, make_int_text, make_short_int_text, param_input_int, 0, 0, 0.0); pval(newg, dist_chosen)->u.i = 0; newg->sd_conn = param_init((Component *)newg, "Standard deviation for conn. if UNIF", (PFD)NULL, make_double_text, make_short_double_text, param_input_double, 0, 0, 0.0); pval(newg, sd_conn)->u.d = 0.0; newg->sd_packet = param_init((Component *)newg, "Standard deviation for packets if UNIF", (PFD)NULL, make_double_text, make_short_double_text, param_input_double, 0, 0, 0.0); pval(newg, sd_packet)->u.d = 0.0; newg->sd_response = param_init((Component *)newg, "Standard deviation for responses if UNIF", (PFD)NULL, make_double_text, make_short_double_text, param_input_double, 0, DisplayMask | ModifyMask, 0.0); pval(newg, sd_response)->u.d = 0.0; newg->telnet_current_train = param_init((Component *)newg, "Current connection", int_calc, make_int_text, make_short_int_text, (PFI)NULL, TIME_HISTORY, 0, 0.0); pval(newg, telnet_current_train)->u.i = 0; newg->telnet_car_how_many = param_init((Component *)newg, "No of packets in this conn.", (PFD)NULL, make_int_text, make_short_int_text, param_input_int, 0, 0, 0.0); pval(newg, telnet_car_how_many)->u.i = 0; newg->telnet_current_car = param_init((Component *)newg, "Current packet in this conn.", int_calc, make_int_text, make_short_int_text, (PFI)NULL, TIME_HISTORY, 0, 0.0); pval(newg, telnet_current_car)->u.i = 0; newg->telnet_produce_ws = param_init((Component *)newg, "Max Produce w size (-1 inf)", (PFD)NULL, make_int_text, make_short_int_text, param_input_int, 0, 0, 0.0); pval(newg, telnet_produce_ws)->u.i = 20; newg->telnet_send_ws = param_init((Component *)newg, "Max Send w size (-1 inf)", (PFD)NULL, make_int_text, make_short_int_text, param_input_int, 0, 0, 0.0); pval(newg, telnet_send_ws)->u.i = 30; newg->telnet_packets_produced = param_init((Component *)newg, "Packets Produced", int_calc, make_int_text, make_short_int_text, (PFI)NULL, TIME_HISTORY, DisplayMask | CanHaveMeterMask | CanHaveLogMask, 0.0); pval(newg, telnet_packets_produced)->u.i = 0; newg->telnet_packets_sent = param_init((Component *)newg, "Packets Sent", int_calc, make_int_text, make_short_int_text, (PFI)NULL, TIME_HISTORY, DisplayMask | CanHaveMeterMask | CanHaveLogMask, 0.0); pval(newg, telnet_packets_sent)->u.i = 0; newg->telnet_packets_acked = param_init((Component *)newg, "Packets Acked", int_calc, make_int_text, make_short_int_text, (PFI)NULL, TIME_HISTORY, DisplayMask | CanHaveMeterMask | CanHaveLogMask, 0.0); pval(newg, telnet_packets_acked)->u.i = 0; newg->telnet_packets_recvd = param_init((Component *)newg, "Packets Received", int_calc, make_int_text, make_short_int_text, (PFI)NULL, TIME_HISTORY, DisplayMask | CanHaveMeterMask | CanHaveLogMask, 0.0); pval(newg, telnet_packets_recvd)->u.i = 0; newg->telnet_packets_retransmitted = param_init((Component *)newg, "Packets Retransmitted", int_calc, make_int_text, make_short_int_text, (PFI)NULL, TIME_HISTORY, DisplayMask | CanHaveMeterMask | CanHaveLogMask, 0.0); pval(newg, telnet_packets_retransmitted)->u.i = 0; newg->telnet_token_time = 0; newg->telnet_rtt = param_init((Component *)newg, "Round trip time est in usec", int_calc, make_int_text, make_short_int_text, (PFI)NULL, TIME_HISTORY, 0, 0.0); pval(newg, telnet_rtt)->u.i = 10000; /* imm -- the following parameters are used to compute the instantaneous sent and acked thruputs */ newg->sent_thruput = param_init((Component *)newg, "Instantaneous sent throughput", double_calc, make_double_text, make_short_double_text, (PFI)NULL, TIME_HISTORY, 0, 0.0); pval(newg, sent_thruput)->u.d = 0; newg->acked_thruput = param_init((Component *)newg, "Instantaneous produced throughput", double_calc, make_double_text, make_short_double_text, (PFI)NULL, TIME_HISTORY, 0, 0.0); pval(newg, acked_thruput)->u.d = 0; newg->retransmission_rate = param_init((Component *)newg, "Instantaneous retransmission rate", double_calc, make_double_text, make_short_double_text, (PFI)NULL, TIME_HISTORY, 0, 0.0); pval(newg, retransmission_rate)->u.d = 0; /* Compute instantaneous delay */ newg->inst_delay = param_init((Component *)newg, "Inst delay in msecs", double_calc, make_double_text, make_short_double_text, (PFI)NULL, TIME_HISTORY, 0, 0.0); pval(newg, inst_delay)->u.d = 0.0; newg->last_pkt_delay = 0; newg->data_acked = 0; newg->last_bytes_acked = 0; #ifdef DEBUG dbg_write(debug_log, DBG_INFO, (Component *)newg, "telnet generator initialized"); #endif return((caddr_t)newg); } static caddr_t telnet_sink_start(g) register Telnett *g; { if (g->telnet_neighbors->l_len != 1) { #ifdef DEBUG dbg_write(debug_log, DBG_ERR, (Component *)g, "can't receive packets-- no neighbors."); #endif return((caddr_t)NULL); } if (g->dest_socket.so_host == (Component *)NULL) { #ifdef DEBUG dbg_write(debug_log, DBG_ERR, (Component *)g, "can't generate packets-- no peer."); #endif return((caddr_t)NULL); } /* Something non-NULL to return */ pm((Component *)g, TELNET_SINK, NEW_COMPONENT, 0, 0, 0, 0); return((caddr_t)g); } static caddr_t telnet_consume(g, pkt) /* consume a packet */ register Telnett *g; Packet *pkt; { if (pkt->tr_pk.response) { ev_enqueue(EV_APTR_PRODUCE, (Component *)g, (Component *)g, ev_now() + ticks_to_response(g), g->telnet_action, (Packet *)NULL, (caddr_t) g); } pk_free(pkt); return((caddr_t)g); } static caddr_t telnet_receive(g, c, pkt) /* receive a packet */ register Telnett *g; register Component *c; register Packet *pkt; { register unsigned int time_now; unsigned int sentTime, DataSize; switch (pkt->tr_pk.tr_type) { case TR_DATA : sentTime = pkt->pk_sent_time; DataSize = pkt->tr_pk.data_size; g->telnet_packets_recvd->u.i ++; log_param((Component *) g, g->telnet_packets_recvd); ev_call(EV_APTR_CONSUME, (Component *)g, (Component *)g, g->telnet_action, pkt, (caddr_t)NULL); pkt = pk_alloc(); pkt->pk_sent_time = sentTime; pkt->pk_length = ACK_PKT_SIZE; pkt->pk_type = TR_PACKET; pkt->tr_pk.tr_type = TR_ACK; pkt->tr_pk.response = 0; pkt->tr_pk.data_size= DataSize; pkt->pk_source_socket.so_host = g->source_socket.so_host; pkt->pk_source_socket.so_port = g->source_socket.so_port; pkt->pk_dest_socket.so_host = g->dest_socket.so_host; pkt->pk_dest_socket.so_port = g->dest_socket.so_port; ev_call(EV_NODE_PRODUCE, (Component *)g, c, c->co_action, pkt, (caddr_t)NULL); #ifdef DEBUG dbg_write(debug_log, DBG_INFO, (Component *)g, "sent an ack"); #endif if (g->telnet_type == TELNET_SOURCE && g->telnet_current_car->u.i == g->no_of_total_cars && g->telnet_packets_produced->u.i == g->telnet_packets_sent->u.i && g->telnet_packets_sent->u.i == g->telnet_packets_acked->u.i && g->telnet_packets_produced->u.i == g->telnet_packets_recvd->u.i) /* a response is received for everything produced */ telnet_conn_down(g); break; case TR_ACK : g->telnet_packets_acked->u.i ++; g->data_acked += pkt->tr_pk.data_size; g->last_bytes_acked = pkt->tr_pk.data_size; pm((Component *)g, TELNET_SOURCE, ACK_RECEIVED, pkt->tr_pk.data_size, g->last_pkt_delay, 0, g->telnet_select->u.i); log_param((Component *) g, g->telnet_packets_acked); /* imm -- compute the average delay of a packet for this connection (the time delay between entering the network and being acked )*/ g->total_delay += (ev_now() - pkt->pk_sent_time); g->recent_pkts_acked ++; g->last_pkt_delay = (ev_now() - pkt->pk_sent_time); pk_free(pkt); ev_call(EV_APTR_SEND, (Component *)g, (Component *)g, g->telnet_action, (Packet *)NULL, (caddr_t)NULL); break; case TR_TOKEN : time_now = ev_now(); g->telnet_rtt->u.i = ((time_now - g->telnet_token_time) * USECS_PER_TICK + g->telnet_rtt->u.i) / 2; log_param((Component *) g, g->telnet_rtt); g->telnet_token_time = time_now; /* dest is source, source is dest */ pkt->pk_source_socket.so_host = g->source_socket.so_host; pkt->pk_source_socket.so_port = g->source_socket.so_port; pkt->pk_dest_socket.so_host = g->dest_socket.so_host; pkt->pk_dest_socket.so_port = g->dest_socket.so_port; ev_call(EV_NODE_PRODUCE, (Component *)g, c, c->co_action, pkt, (caddr_t)NULL); break; default : break; } return((caddr_t)g); }