/* Handle an incoming connection. * * return -1 if no crypto inbound connection. * return incoming connection id (Lossless_UDP one) if there is an incoming crypto connection. * * Put the public key of the peer in public_key, the secret_nonce from the handshake into secret_nonce * and the session public key for the connection in session_key. * to accept it see: accept_crypto_inbound(...). * to refuse it just call kill_connection(...) on the connection id. */ int crypto_inbound(Net_Crypto *c, uint8_t *public_key, uint8_t *secret_nonce, uint8_t *session_key) { while (1) { int incoming_con = incoming_connection(c->lossless_udp, 1); if (incoming_con != -1) { if (is_connected(c->lossless_udp, incoming_con) == LUDP_TIMED_OUT) { kill_connection(c->lossless_udp, incoming_con); continue; } if (id_packet(c->lossless_udp, incoming_con) == 2) { uint8_t temp_data[MAX_DATA_SIZE]; uint16_t len = read_packet_silent(c->lossless_udp, incoming_con, temp_data); if (handle_cryptohandshake(c, public_key, secret_nonce, session_key, temp_data, len)) { return incoming_con; } else { kill_connection(c->lossless_udp, incoming_con); } } else { kill_connection(c->lossless_udp, incoming_con); } } else { break; } } return -1; }
static void reset_connection(struct argos_net_conn *conn, int flush_buffers) { if (flush_buffers) { buffer_empty(conn->inbuf); buffer_empty(conn->outbuf); buffer_empty(conn->pktbuf); } else { if (conn->outbuf_unsync) { /* * Even if flush_buffers=0, we still need to flush the outbuf if its * unsynced (otherwise we might get out of sync when we resume * sending to the server after reconnecting). */ buffer_empty(conn->outbuf); } /* * always flush inbuf for the same reason (could use a variable to avoid * unnecessary flushes...) */ buffer_empty(conn->inbuf); } /* if connection is shutting down, check if outbuf is now empty */ if (conn->shutdown && buffers_are_empty(conn)) { kill_connection(conn); return; } close_socket(conn->sock); conn->sock = 0; conn->status_flags |= ARGOS_NET_STATS_CONN_DOWN; /* schedule an reconnection event */ conn->state = ARGOS_NET_CONN_BACKOFF; conn->reconnect_evt_reg = async_schedule_sec(conn->cur_backoff, reconnect_event, conn, 0); if (conn->reconnect_evt_reg == NULL) { orion_log_errno("async_schedule_sec"); kill_connection(conn); orion_log_err("failed to schedule reconnection event" "; connection is now dead"); } /* exponentially increase backoff */ conn->cur_backoff *= 2; if (conn->cur_backoff > conn->max_backoff) conn->cur_backoff = conn->max_backoff; /* make this callback last */ if (conn->breakhandler != NULL) conn->breakhandler(conn, conn->breakhandler_user); }
/* Kill a crypto connection. * * return 0 if killed successfully. * return 1 if there was a problem. */ int crypto_kill(Net_Crypto *c, int crypt_connection_id) { if (crypt_connection_id_not_valid(c, crypt_connection_id)) return 1; if (c->crypto_connections[crypt_connection_id].status != CRYPTO_CONN_NO_CONNECTION) { c->crypto_connections[crypt_connection_id].status = CRYPTO_CONN_NO_CONNECTION; kill_connection(c->lossless_udp, c->crypto_connections[crypt_connection_id].number); memset(&(c->crypto_connections[crypt_connection_id]), 0 , sizeof(Crypto_Connection)); c->crypto_connections[crypt_connection_id].number = ~0; uint32_t i; for (i = c->crypto_connections_length; i != 0; --i) { if (c->crypto_connections[i - 1].status != CRYPTO_CONN_NO_CONNECTION) break; } if (c->crypto_connections_length != i) { c->crypto_connections_length = i; realloc_cryptoconnection(c, c->crypto_connections_length); } return 0; } return 1; }
/* handle an incoming connection return -1 if no crypto inbound connection return incoming connection id (Lossless_UDP one) if there is an incoming crypto connection Put the public key of the peer in public_key, the secret_nonce from the handshake into secret_nonce and the session public key for the connection in session_key to accept it see: accept_crypto_inbound(...) to refuse it just call kill_connection(...) on the connection id */ int crypto_inbound(uint8_t * public_key, uint8_t * secret_nonce, uint8_t * session_key) { uint32_t i; for(i = 0; i < MAX_INCOMING; ++i) { if(incoming_connections[i] != -1) { if(is_connected(incoming_connections[i]) == 4 || is_connected(incoming_connections[i]) == 0) { kill_connection(incoming_connections[i]); incoming_connections[i] = -1; continue; } if(id_packet(incoming_connections[i]) == 2) { uint8_t temp_data[MAX_DATA_SIZE]; uint16_t len = read_packet(incoming_connections[i], temp_data); if(handle_cryptohandshake(public_key, secret_nonce, session_key, temp_data, len)) { int connection_id = incoming_connections[i]; incoming_connections[i] = -1; /* remove this connection from the incoming connection list. */ return connection_id; } } } } return -1; }
void argos_net_shutdown(struct argos_net_conn *conn) { if (conn->shutdown == 0) { conn->shutdown = 1; if (buffers_are_empty(conn)) kill_connection(conn); } }
static void killTimedout(void) { uint32_t i; for (i = 0; i < MAX_CRYPTO_CONNECTIONS; ++i) { if (crypto_connections[i].status != CONN_NO_CONNECTION && is_connected(crypto_connections[i].number) == 4) crypto_connections[i].status = CONN_TIMED_OUT; else if (is_connected(crypto_connections[i].number) == 4) { kill_connection(crypto_connections[i].number); crypto_connections[i].number = ~0; } } }
/* kill a crypto connection return 0 if killed successfully return 1 if there was a problem. */ int crypto_kill(int crypt_connection_id) { if (crypt_connection_id < 0 || crypt_connection_id >= MAX_CRYPTO_CONNECTIONS) return 1; if (crypto_connections[crypt_connection_id].status != CONN_NO_CONNECTION) { crypto_connections[crypt_connection_id].status = CONN_NO_CONNECTION; kill_connection(crypto_connections[crypt_connection_id].number); memset(&crypto_connections[crypt_connection_id], 0 ,sizeof(Crypto_Connection)); crypto_connections[crypt_connection_id].number = ~0; return 0; } return 1; }
void argos_net_close(struct argos_net_conn *conn) { orion_log_func(); if (conn->state != ARGOS_NET_CONN_DEAD) kill_connection(conn); buffer_destroy(conn->inbuf); buffer_destroy(conn->outbuf); buffer_destroy(conn->pktbuf); free(conn); }
static ssize_t socket_send(struct argos_net_conn *conn, const void *msg, size_t len) { #if ARGOS_NET_TRACE_IO struct timeval start; if (gettimeofday(&start, NULL) != 0) { orion_log_crit_errno("gettimeofday"); return -1; } #endif /* #if ARGOS_NET_TRACE_IO */ ssize_t sentlen = send(conn->sock, msg, len, 0); if (sentlen == -1) { if (IS_NETWORK_ERROR(errno)) { /* network error; reset our connection */ orion_log_warn_errno("send() failed"); reset_connection(conn, 0); } else { /* anything else is a fatal error */ orion_log_crit_errno("send"); kill_connection(conn); orion_log_crit("unexpected send() error; connection is now dead"); } return -1; } else { /* send() succeeded */ assert(sentlen > 0); /* this variable is used even if ARGOS_NET_TRACE_IO is false */ struct timeval end; if (gettimeofday(&end, NULL) != 0) { orion_log_crit_errno("gettimeofday"); return -1; } #if ARGOS_NET_TRACE_IO struct timeval elapsed; orion_time_subtract(&end, &start, &elapsed); float elapsed_msec = elapsed.tv_sec*1000 + (float)elapsed.tv_usec/1000; orion_log_debug("sent %u bytes in %.2f ms (%.2f MB/s); requested %u", sentlen, elapsed_msec, ((sentlen/elapsed_msec)*1000)/(1024*1024), len); #endif /* #if ARGOS_NET_TRACE_IO */ conn->bytes_sent += sentlen; conn->last_send = end; conn->stall_logged = 0; return sentlen; } }
static void kill_timedout(Net_Crypto *c) { uint32_t i; for (i = 0; i < c->crypto_connections_length; ++i) { if (c->crypto_connections[i].status != CONN_NO_CONNECTION && is_connected(c->lossless_udp, c->crypto_connections[i].number) == 4) c->crypto_connections[i].status = CONN_TIMED_OUT; else if (is_connected(c->lossless_udp, c->crypto_connections[i].number) == 4) { kill_connection(c->lossless_udp, c->crypto_connections[i].number); c->crypto_connections[i].number = ~0; } } }
static void write_cb(int fd, void *arg) { struct argos_net_conn *conn = arg; if (conn->state == ARGOS_NET_CONN_CONNECTING) { handle_connect(conn); } else if (conn->state == ARGOS_NET_CONN_CONNECTED) { /* this callback shouldn't happen unless outbuf is non-empty */ size_t datalen = buffer_len(conn->outbuf); assert(datalen > 0); /* * when possible, we want to make sure to feed send() large blocks of * data at a time, so if there is only a little data in the outbuf, try * to get some more by compressing and moving over some of the pktbuf */ if (datalen < SEND_SOFT_MIN) { (void) compress_and_xfer(conn, 0 /* don't force */); datalen = buffer_len(conn->outbuf); } ssize_t len = socket_send(conn, buffer_head(conn->outbuf), datalen); if (len != -1) { if (buffer_discard(conn->outbuf, len) == -1) KABOOM("buffer_discard"); /* * When a partial-send occurs, this means we might have sent part of * a message and left the remainder sitting at the head of the * outbuf. This is not a problem for the server (it will receive * and buffer the portion that was sent, waiting for us to send the * remainder) - however, it means that we cannot arbitrarily send * things down the socket until we finish off this partially-sent * message. We call this state "unsynced" because we don't know if * we stopped sending on a message boundary or not. */ if (buffer_len(conn->outbuf) == 0) conn->outbuf_unsync = 0; else if (datalen != len) conn->outbuf_unsync = 1; /* if connection is shutting down, check if buffers are now empty */ if (conn->shutdown && buffers_are_empty(conn)) kill_connection(conn); } } }
/* kill a crypto connection return 0 if killed successfully return 1 if there was a problem. */ int crypto_kill(int crypt_connection_id) { if(crypt_connection_id < 0 || crypt_connection_id >= MAX_CRYPTO_CONNECTIONS) { return 1; } if(crypto_connections[crypt_connection_id].status != 0) { crypto_connections[crypt_connection_id].status = 0; kill_connection(crypto_connections[crypt_connection_id].number); crypto_connections[crypt_connection_id].number = ~0; return 0; } return 1; }
static void killTimedout() { uint32_t i; for(i = 0; i < MAX_CRYPTO_CONNECTIONS; ++i) { if(crypto_connections[i].status != 0 && is_connected(crypto_connections[i].number) == 4) { crypto_connections[i].status = 4; } else if(is_connected(crypto_connections[i].number) == 4) { kill_connection(crypto_connections[i].number); crypto_connections[i].number = ~0; } } }
static void read_cb(int fd, void *arg) { struct argos_net_conn *conn = arg; /* * We only want to do reads if conn->state == NET_CONN_CONNECTED, but we * don't assert() this because its possible for this socket to be selected * simultaneously for both a read and a write and then for our state to * change during the write attempt. */ if (conn->state != ARGOS_NET_CONN_CONNECTED) return; ssize_t len = recv(conn->sock, buffer_tail(conn->inbuf), buffer_remaining(conn->inbuf), 0); if (len == -1) { if (IS_NETWORK_ERROR(errno)) { /* network error; reset our connection */ orion_log_warn_errno("recv"); reset_connection(conn, 0); } else if (errno == EINTR) { /* don't care; ignore it */ } else { /* anything else is a fatal error */ orion_log_crit_errno("recv"); kill_connection(conn); orion_log_crit("unexpected recv() error; connection is now dead"); } } else if (len == 0) { /* EOF received (maybe other end is shutting down?) */ orion_log_info("EOF received from remote end - closing socket"); if (buffer_len(conn->inbuf) > 0) orion_log_warn("incomplete message received (inbuflen=%d)", buffer_len(conn->inbuf)); reset_connection(conn, 1 /* flush buffers */); } else { /* ok, we read some data into the inbuf; update the buffer */ int rv = buffer_expand(conn->inbuf, len); if (rv == -1) KABOOM("buffer_expand"); conn->bytes_recv += len; /* now process (i.e. look for complete messages in) the inbuf */ process_inbuf(conn); } }
static void handle_connect(struct argos_net_conn *conn) { assert(conn->state == ARGOS_NET_CONN_CONNECTING); /* * connect() completed, but did it succeed or fail? getpeername() will * tell us. reference: http://cr.yp.to/docs/connect.html */ struct sockaddr_in sin; socklen_t slen = sizeof(sin); if (getpeername(conn->sock, (struct sockaddr*)&sin, &slen) == -1) { if (errno == ENOTCONN) { /* connect failed; ok now use error slippage to get the real error */ char c; int rv = read(conn->sock, &c, 1); assert(rv == -1); handle_connect_failure(conn); } else if (errno == ECONNRESET) { /* * not sure if this can actually happen - perhaps with perfect * timing (connection lost right before we call getpeername) */ orion_log_warn_errno("getpeername"); reset_connection(conn, 0); } else { /* this is unexpected... */ orion_log_crit_errno("getpeername"); kill_connection(conn); orion_log_crit("unexpected getpeername() error after asynchronous" " connect() selected for writability; connection is now dead"); } } else { /* connect succeeded */ orion_log_info("connect() succeeded asynchronously"); handle_connect_success(conn); } }
static int attempt_connect(struct argos_net_conn *conn) { if (!conn->connect_failed) orion_log_func(); assert(conn->state == ARGOS_NET_CONN_IDLE); /* create and set up the actual socket */ conn->sock = socket(AF_INET, SOCK_STREAM, 0); if (conn->sock < 0) { orion_log_errno("socket"); goto fail; } /* set non-blocking on socket */ int status = fcntl(conn->sock, F_GETFL, NULL); if (status < 0) { orion_log_crit_errno("fcntl(F_GETFL)"); goto fail; } status |= O_NONBLOCK; if (fcntl(conn->sock, F_SETFL, status) < 0) { orion_log_crit_errno("fcntl(F_SETFL)"); goto fail; } /* prevent socket from throwing SIGPIPE signals */ int on = 1; if (setsockopt(conn->sock, SOL_SOCKET, SO_NOSIGPIPE, (void *)&on, sizeof(on)) < 0) { orion_log_crit_errno("setsockopt(SO_NOSIGPIPE)"); goto fail; } /* select for writes (that's how completion of a connect() is signaled) */ int rv = async_add_write_fd(conn->sock, ARGOS_NET_CONNECT_ASYNCPRIO, writable_cb, write_cb, conn); if (rv != 0) { orion_log_errno("async_add_write_fd"); async_remove_fd(conn->sock); goto fail; } /* finally ready to attempt the connect() call */ rv = connect(conn->sock, (struct sockaddr*)&conn->remote_addr, sizeof(conn->remote_addr)); if (rv == -1) return handle_connect_failure(conn); /* else, rv=0 which means instant success (odd...) */ orion_log_info("connect() succeeded immediately"); handle_connect_success(conn); return 0; fail: /* * something very bad happened; presumably either there is a code bug or the * OS is in bad shape (e.g. out of memory, no more file descriptors, etc.) */ kill_connection(conn); orion_log_crit("failed to create network client"); return -1; }
/* Display the menu that allows the user to act on a connection. */ static void statevent_interactivemenu_conn( const int debuglvl, struct vuurmuur_config *cnf, StatEventCtl *ctl, Conntrack *ct, VR_ConntrackRequest *connreq, Zones *zones, BlockList *blocklist, Interfaces *interfaces, Services *services, StatEventGen *gen_ptr) { VrWin *win = NULL; VrMenu *menu = NULL; int ch = 0; int menu_items = 10; char *str = NULL; const int width = 70; /* top menu */ char *key_choices[] = { "F12", "F10"}; int key_choices_n = 2; char *cmd_choices[] = { gettext("help"), gettext("back")}; int cmd_choices_n = 2; StatEventConn *con = (StatEventConn *)gen_ptr; Conntrack *privct = NULL; char ungroup_conns = FALSE; char *title = gettext("Manage Connection"); /* if needed get our own private ungrouped ct */ if(connreq->group_conns == TRUE) { ungroup_conns = TRUE; /* we are ungrouping the list */ connreq->group_conns = FALSE; privct = conn_init_ct(debuglvl, zones, interfaces, services, blocklist); if(privct == NULL) return; conn_ct_get_connections(debuglvl, cnf, privct, connreq); ct = privct; } /* create the window and put it in the middle of the screen */ win = VrNewWin(menu_items + 2,width,0,0,vccnf.color_win); if(win == NULL) { (void)vrprint.error(-1, VR_ERR, "VrNewWin failed"); return; } VrWinSetTitle(win, title); menu = VrNewMenu(menu_items, width - 2, 1,1, menu_items,vccnf.color_win,vccnf.color_win_rev); if(menu == NULL) { (void)vrprint.error(-1, VR_ERR, "VrNewMenu failed"); return; } VrMenuSetDescFreeFunc(menu, free); VrMenuSetupNameList(debuglvl, menu); VrMenuSetupDescList(debuglvl, menu); /* setup menu items */ if(con->cnt == 1) str = VrGetString(gettext("Kill this connection")); else str = VrGetString(gettext("Kill all connections with this service/source/destination"),con->cnt); VrMenuAddItem(debuglvl, menu, "1", str); str = VrGetString("--- %s ---", gettext("Kill options")); VrMenuAddSepItem(debuglvl, menu, str); str = VrGetString(gettext("Kill all connections with source %s"), con->src_ip); VrMenuAddItem(debuglvl, menu, "2", str); str = VrGetString(gettext("Kill all connections with destination %s"), con->dst_ip); VrMenuAddItem(debuglvl, menu, "3", str); str = VrGetString(gettext("Kill all connections of %s"), con->src_ip); VrMenuAddItem(debuglvl, menu, "4", str); str = VrGetString(gettext("Kill all connections of %s"), con->dst_ip); VrMenuAddItem(debuglvl, menu, "5", str); str = VrGetString("--- %s ---", gettext("BlockList options")); VrMenuAddSepItem(debuglvl, menu, str); str = VrGetString(gettext("Add source %s to BlockList"), con->src_ip); VrMenuAddItem(debuglvl, menu, "6", str); str = VrGetString(gettext("Add destination %s to BlockList"), con->dst_ip); VrMenuAddItem(debuglvl, menu, "7", str); str = VrGetString(gettext("Add both source and destination to BlockList")); VrMenuAddItem(debuglvl, menu, "8", str); VrMenuConnectToWin(debuglvl, menu, win); VrMenuPost(debuglvl, menu); draw_top_menu(debuglvl, top_win, title, key_choices_n, key_choices, cmd_choices_n, cmd_choices); update_panels(); doupdate(); /* user input */ char quit = FALSE; while(quit == FALSE) { ch = VrWinGetch(win); switch(ch) { case 27: case 'q': case 'Q': case KEY_F(10): quit = TRUE; break; case 10: { ITEM *cur = current_item(menu->m); if(cur != NULL) { int act = atoi((char *)item_name(cur)); switch(act) { case 1: /* kill */ { /* check if the conntrack tool is set */ if(conf.conntrack_location[0] == '\0') { (void)vrprint.error(-1, VR_ERR, STR_CONNTRACK_LOC_NOT_SET); } else if(con->cnt == 1) { if(confirm(gettext("Kill connection"),gettext("Are you sure?"), vccnf.color_win_note, vccnf.color_win_note_rev|A_BOLD, 1) == 1) { kill_connection(debuglvl, conf.conntrack_location, con->src_ip, con->dst_ip, con->protocol, con->src_port, con->dst_port); } } else { vrprint.debug(__FUNC__, "cnt %u, src %s srcip %s dst %s dstip %s ser %s", con->cnt, con->src, con->src_ip, con->dst, con->dst_ip, con->ser); if(confirm(gettext("Kill connections"),gettext("Are you sure?"), vccnf.color_win_note, vccnf.color_win_note_rev|A_BOLD, 1) == 1) { kill_connections(debuglvl, &conf, connreq, ct, con); } } break; } case 2: /* kill all src ip */ /* check if the conntrack tool is set */ if(conf.conntrack_location[0] == '\0') { (void)vrprint.error(-1, VR_ERR, STR_CONNTRACK_LOC_NOT_SET); } else if(confirm(gettext("Kill connections"),gettext("Are you sure?"), vccnf.color_win_note, vccnf.color_win_note_rev|A_BOLD, 1) == 1) { kill_connections_by_ip(debuglvl, &conf, ct, con->src_ip, NULL, NULL, CONN_UNUSED); } break; case 3: /* kill all dst ip */ /* check if the conntrack tool is set */ if(conf.conntrack_location[0] == '\0') { (void)vrprint.error(-1, VR_ERR, STR_CONNTRACK_LOC_NOT_SET); } else if(confirm(gettext("Kill connections"),gettext("Are you sure?"), vccnf.color_win_note, vccnf.color_win_note_rev|A_BOLD, 1) == 1) { kill_connections_by_ip(debuglvl, &conf, ct, NULL, con->dst_ip, NULL, CONN_UNUSED); } break; case 4: /* check if the conntrack tool is set */ if(conf.conntrack_location[0] == '\0') { (void)vrprint.error(-1, VR_ERR, STR_CONNTRACK_LOC_NOT_SET); } else if(confirm(gettext("Kill connections"),gettext("Are you sure?"), vccnf.color_win_note, vccnf.color_win_note_rev|A_BOLD, 1) == 1) { kill_connections_by_ip(debuglvl, &conf, ct, NULL, con->src_ip, NULL, CONN_UNUSED); kill_connections_by_ip(debuglvl, &conf, ct, con->src_ip, NULL, NULL, CONN_UNUSED); } break; case 5: /* check if the conntrack tool is set */ if(conf.conntrack_location[0] == '\0') { (void)vrprint.error(-1, VR_ERR, STR_CONNTRACK_LOC_NOT_SET); } else if(confirm(gettext("Kill connections"),gettext("Are you sure?"), vccnf.color_win_note, vccnf.color_win_note_rev|A_BOLD, 1) == 1) { kill_connections_by_ip(debuglvl, &conf, ct, NULL, con->dst_ip, NULL, CONN_UNUSED); kill_connections_by_ip(debuglvl, &conf, ct, con->dst_ip, NULL, NULL, CONN_UNUSED); } break; case 6: if(confirm(gettext("Add to BlockList and Apply Changes"),gettext("Are you sure?"), vccnf.color_win_note, vccnf.color_win_note_rev|A_BOLD, 1) == 1) { block_and_kill(debuglvl, ct, zones, blocklist, interfaces, con->src_ip); } break; case 7: if(confirm(gettext("Add to BlockList and Apply Changes"),gettext("Are you sure?"), vccnf.color_win_note, vccnf.color_win_note_rev|A_BOLD, 1) == 1) { block_and_kill(debuglvl, ct, zones, blocklist, interfaces, con->dst_ip); } break; case 8: if(confirm(gettext("Add to BlockList and Apply Changes"),gettext("Are you sure?"), vccnf.color_win_note, vccnf.color_win_note_rev|A_BOLD, 1) == 1) { block_and_kill(debuglvl, ct, zones, blocklist, interfaces, con->src_ip); block_and_kill(debuglvl, ct, zones, blocklist, interfaces, con->dst_ip); } break; default: break; } } break; } case KEY_F(12): case 'h': case 'H': case '?': print_help(debuglvl, ctl->help_actions); break; default: (void)VrMenuDefaultNavigation(debuglvl, menu, ch); break; } } VrDelMenu(debuglvl, menu); VrDelWin(win); update_panels(); doupdate(); /* we have ungrouped the list, clean up here. */ if(ungroup_conns == TRUE) { connreq->group_conns = TRUE; conn_ct_clear_connections(debuglvl, privct); conn_free_ct(debuglvl, &privct, NULL); } return; }
void test_send_pdu(struct proc *p, iscsi_test_send_pdu_parameters_t *par) { static uint8_t pad_bytes[4] = { 0 }; test_pars_t *tp; connection_t *conn; pdu_t *pdu; uint32_t psize = par->pdu_size; void *pdu_ptr = par->pdu_ptr; struct uio *uio; uint32_t i, pad, dsl, size; int s; if ((tp = find_test_id(par->test_id)) == NULL) { par->status = ISCSI_STATUS_INVALID_ID; return; } if (!psize || pdu_ptr == NULL || ((par->options & ISCSITEST_SFLAG_UPDATE_FIELDS) && psize < BHS_SIZE)) { par->status = ISCSI_STATUS_PARAMETER_INVALID; return; } if ((conn = tp->connection) == NULL) { par->status = ISCSI_STATUS_TEST_INACTIVE; return; } if ((pdu = get_pdu(conn, TRUE)) == NULL) { par->status = ISCSI_STATUS_TEST_CONNECTION_CLOSED; return; } DEB(1, ("Test Send PDU, id %d\n", par->test_id)); if ((par->status = map_databuf(p, &pdu_ptr, psize)) != 0) { free_pdu(pdu); return; } i = 1; if (!par->options) { pdu->io_vec[0].iov_base = pdu_ptr; pdu->io_vec[0].iov_len = size = psize; } else { memcpy(&pdu->pdu, pdu_ptr, BHS_SIZE); if (!(pdu->pdu.Opcode & OP_IMMEDIATE)) conn->session->CmdSN++; pdu->pdu.p.command.CmdSN = htonl(conn->session->CmdSN); dsl = psize - BHS_SIZE; size = BHS_SIZE; hton3(dsl, pdu->pdu.DataSegmentLength); if (conn->HeaderDigest && !(par->options & ISCSITEST_SFLAG_NO_HEADER_DIGEST)) { pdu->pdu.HeaderDigest = gen_digest(&pdu->pdu, BHS_SIZE); size += 4; } pdu->io_vec[0].iov_base = &pdu->pdu; pdu->io_vec[0].iov_len = size; if (dsl) { pdu->io_vec[1].iov_base = &pdu_ptr[BHS_SIZE]; pdu->io_vec[1].iov_len = dsl; i++; size += dsl; /* Pad to next multiple of 4 */ pad = (par->options & ISCSITEST_SFLAG_NO_PADDING) ? 0 : size & 0x03; if (pad) { pad = 4 - pad; pdu->io_vec[i].iov_base = pad_bytes; pdu->io_vec[i].iov_len = pad; i++; size += pad; } if (conn->DataDigest && !(par->options & ISCSITEST_SFLAG_NO_DATA_DIGEST)) { pdu->data_digest = gen_digest_2(&pdu_ptr[BHS_SIZE], dsl, pad_bytes, pad); pdu->io_vec[i].iov_base = &pdu->data_digest; pdu->io_vec[i].iov_len = 4; i++; size += 4; } } } uio = &pdu->uio; uio->uio_iov = pdu->io_vec; UIO_SETUP_SYSSPACE(uio); uio->uio_rw = UIO_WRITE; uio->uio_iovcnt = i; uio->uio_resid = size; pdu->disp = PDUDISP_SIGNAL; pdu->flags = PDUF_BUSY | PDUF_NOUPDATE; s = splbio(); /* Enqueue for sending */ if (pdu->pdu.Opcode & OP_IMMEDIATE) TAILQ_INSERT_HEAD(&conn->pdus_to_send, pdu, send_chain); else TAILQ_INSERT_TAIL(&conn->pdus_to_send, pdu, send_chain); wakeup(&conn->pdus_to_send); tsleep(pdu, PINOD, "test_send_pdu", 0); splx(s); unmap_databuf(p, pdu_ptr, psize); par->status = ISCSI_STATUS_SUCCESS; if (par->options & ISCSITEST_KILL_CONNECTION) kill_connection(conn, ISCSI_STATUS_TEST_CONNECTION_CLOSED, NO_LOGOUT, TRUE); }
static void process_inbuf(struct argos_net_conn *conn) { while (buffer_len(conn->inbuf) >= sizeof(struct argos_net_minimal_msg)) { struct argos_net_minimal_msg *header = (struct argos_net_minimal_msg *)buffer_head(conn->inbuf); uint16_t msgtype = ntohs(header->msgtype); uint32_t msglen = ntohl(header->msglen); /* check that message type and length are valid */ if (ARGOS_NET_VALIDATE_MSGTYPE(msgtype) == 0) { orion_log_crit("invalid message type received; type=%hu, len=%u", msgtype, msglen); reset_connection(conn, 1 /* flush buffers */); return; } if (ARGOS_NET_VALIDATE_MSGLEN(msgtype, msglen) == 0) { orion_log_crit("invalid message len received; type=%hu, len=%u", msgtype, msglen); reset_connection(conn, 1 /* flush buffers */); return; } if (msglen > buffer_len(conn->inbuf)) { /* complete message not yet received */ if (msglen > buffer_size(conn->inbuf)) { /* error - message is bigger than the entire inbuf */ orion_log_err("inbuf too small for msgtype %hu (len=%u)", msgtype, msglen); reset_connection(conn, 1 /* flush buffers */); return; } /* wait for more bytes to arrive on socket */ break; } /* ok great, message length must be valid - process it */ switch (msgtype) { case ARGOS_NET_CLOSECONN_MSGTYPE: { struct argos_net_closeconn_msg msg; int rv = buffer_read(conn->inbuf, &msg, sizeof(msg)); assert(rv >= 0); orion_log_info("received close-connection message from server"); reset_connection(conn, 1 /* flush buffers */); break; } case ARGOS_NET_ERROR_MSGTYPE: { struct argos_net_error_msg msg; int rv = buffer_read(conn->inbuf, &msg, sizeof(msg)); assert(rv >= 0); char buf[ARGOS_NET_MAX_ERR_LEN+1]; ssize_t bodylen = msglen - sizeof(msg); rv = buffer_read(conn->inbuf, buf, bodylen); assert(rv >= 0); buf[bodylen] = '\0'; if (conn->errhandler != NULL) conn->errhandler(ntohs(msg.errnum), buf, conn->errhandler_user); else orion_log_err("[server] %s (%s)", strerror(ntohs(msg.errnum)), buf); break; } case ARGOS_NET_HANDSHAKE_MSGTYPE: { struct argos_net_handshake_msg msg; int rv = buffer_read(conn->inbuf, &msg, sizeof(msg)); assert(rv >= 0); /* sniffers should never receive handshake messages */ orion_log_crit("received handshake message from server"); reset_connection(conn, 1 /* flush buffers */); break; } case ARGOS_NET_PCAP_MSGTYPE: { struct argos_net_pcap_msg msg; int rv = buffer_read(conn->inbuf, &msg, sizeof(msg)); assert(rv >= 0); size_t bodylen = msglen - sizeof(msg); assert(bodylen == ntohl(msg.caplen)); if (conn->pkthandler == NULL) { int rv = buffer_discard(conn->inbuf, bodylen); assert(rv >= 0); break; } struct pcap_pkthdr pcap_hdr; pcap_hdr.len = ntohl(msg.pktlen); pcap_hdr.caplen = ntohl(msg.caplen); pcap_hdr.ts.tv_sec = ntohl(msg.ts_sec); pcap_hdr.ts.tv_usec = ntohl(msg.ts_usec); /* * index directly into buffer if possible (i.e. if the data * doesn't wrap in the buffer) */ if (buffer_len(conn->inbuf) >= bodylen) { conn->pkthandler(&pcap_hdr, buffer_head(conn->inbuf), conn->pkthandler_user); buffer_discard(conn->inbuf, bodylen); } else { /* data wraps; need to copy into a temporary buffer */ u_char tempbuf[ARGOS_NET_MAX_PKT_LEN]; buffer_read(conn->inbuf, tempbuf, bodylen); conn->pkthandler(&pcap_hdr, tempbuf, conn->pkthandler_user); } break; } case ARGOS_NET_SETBPF_MSGTYPE: { struct argos_net_setbpf_msg msg; int rv = buffer_read(conn->inbuf, &msg, sizeof(msg)); assert(rv >= 0); char buf[ARGOS_NET_MAX_BPF_LEN+1]; size_t bodylen = msglen - sizeof(msg); rv = buffer_read(conn->inbuf, buf, bodylen); assert(rv >= 0); buf[bodylen] = '\0'; if (conn->bpfhandler != NULL) conn->bpfhandler(buf, conn->bpfhandler_user); /* else, just discard and ignore */ break; } case ARGOS_NET_SETCHAN_MSGTYPE: { struct argos_net_setchan_msg msg; int rv = buffer_read(conn->inbuf, &msg, sizeof(msg)); assert(rv >= 0); if (conn->chanhandler != NULL) conn->chanhandler(ntohs(msg.chan), conn->chanhandler_user); /* else, just discard and ignore */ break; } case ARGOS_NET_STARTCLICK_MSGTYPE: { struct argos_net_startclick_msg msg; int rv = buffer_read(conn->inbuf, &msg, sizeof(msg)); assert(rv >= 0); size_t bodylen = msglen - sizeof(msg); char *click_conf = malloc(bodylen+1); if (click_conf == NULL) { char errmsg[256]; snprintf(errmsg, sizeof(errmsg), "malloc(%d): %s", bodylen, strerror(errno)); orion_log_crit_errno("malloc()"); argos_net_send_errmsg(conn, errno, errmsg); kill_connection(conn); return; } rv = buffer_read(conn->inbuf, click_conf, bodylen); assert(rv >= 0); click_conf[bodylen] = '\0'; /* is this click config any different from the last one we got? */ uint32_t key = ntohl(msg.key); if (key == conn->click_config_key) { /* keys match; we can ignore this message */ orion_log_debug("click-configuration keys match (%d)", key); } else { /* keys don't match; need to run this new configuration */ orion_log_debug("new click-configuration key: %d", key); conn->click_config_key = key; if (conn->clickhandler != NULL) conn->clickhandler(click_conf, conn->clickhandler_user); } free(click_conf); break; } case ARGOS_NET_STATS_MSGTYPE: { struct argos_net_stats_msg msg; int rv = buffer_read(conn->inbuf, &msg, sizeof(msg)); assert(rv >= 0); orion_log_warn("received stats message from server"); break; } default: orion_log_crit("process_inbuf() of net.c out of sync with net.h;" " no switch case for msgtype %hu", msgtype); kill_connection(conn); } } }
static int handle_connect_failure(struct argos_net_conn *conn) { switch (errno) { /* these errors indicate some kind of programming error */ case EBADF: case ENOTSOCK: case EAFNOSUPPORT: case EFAULT: /* fall through */ /* * generally, these are non-fatal, but should never happen in this * code; if they do, this indicates a programming error */ case EISCONN: case EALREADY: orion_log_crit_errno("connect"); kill_connection(conn); orion_log_crit("unexpected connect() error; connection is now dead"); return -1; /* these are transient errors; we retry connecting */ case EADDRNOTAVAIL: case ETIMEDOUT: case ECONNREFUSED: case ENETUNREACH: case EHOSTUNREACH: case EADDRINUSE: case ECONNRESET: /* * EAGAIN is NOT the same as EINPROGRESS; it means that the server's * connection backlog is full */ case EAGAIN: if (!conn->connect_failed) { orion_log_warn_errno("connect"); conn->connect_failed = 1; } reset_connection(conn, 0); return 0; /* these aren't really errors; the connect() should still work */ case EINPROGRESS: /* * this errno is ok if returned by connect() itself, but shouldn't be * returned via error slippage from a recv() after a failed connect */ if (conn->state == ARGOS_NET_CONN_CONNECTING) { orion_log_crit_errno("read after failed connect"); kill_connection(conn); orion_log_crit("unexpected recv() error; connection is now dead"); } /* fall through */ case EINTR: orion_log_debug("connect() in progress (\"%s\")", strerror(errno)); conn->state = ARGOS_NET_CONN_CONNECTING; return 0; default: /* unknown errno */ orion_log_crit_errno("read after failed connect"); orion_log_crit("unexpected connect errno"); kill_connection(conn); return -1; } }
STATIC int test_mod(test_pars_t *tp, pdu_t *pdu, iscsi_pdu_kind_t kind, int rxtx, int err) { mod_desc_t *mod; uint32_t mpoff, off; int i, rc = 0, s; tp->pdu_count[kind][rxtx]++; tp->pdu_count[ANY_PDU][rxtx]++; do { if ((mod = TAILQ_FIRST(&tp->mods)) == NULL) { return check_loss(tp, rxtx); } if (mod->pars.which_pdu != ANY_PDU && mod->pars.which_pdu != kind) { return check_loss(tp, rxtx); } mpoff = mod->pars.pdu_offset; switch (mod->pars.which_offset) { case ABSOLUTE_ANY: off = tp->pdu_count[ANY_PDU][CNT_TX] + tp->pdu_count[ANY_PDU][CNT_RX]; break; case RELATIVE_ANY: off = (tp->pdu_count[ANY_PDU][CNT_TX] + tp->pdu_count[ANY_PDU][CNT_RX]) - (tp->pdu_last[ANY_PDU][CNT_TX] + tp->pdu_last[ANY_PDU][CNT_RX]); break; case ABSOLUTE_PDUKIND: off = tp->pdu_count[kind][rxtx]; break; case RELATIVE_PDUKIND: off = tp->pdu_count[kind][rxtx] - tp->pdu_last[kind][rxtx]; break; case ABSOLUTE_TX: if (rxtx != CNT_TX) return check_loss(tp, rxtx); off = tp->pdu_count[ANY_PDU][CNT_TX]; break; case RELATIVE_TX: if (rxtx != CNT_TX) return check_loss(tp, rxtx); off = tp->pdu_count[ANY_PDU][CNT_TX] - tp->pdu_last[ANY_PDU][CNT_TX]; break; case ABSOLUTE_RX: if (rxtx != CNT_RX) return check_loss(tp, rxtx); off = tp->pdu_count[ANY_PDU][CNT_RX]; break; case RELATIVE_RX: if (rxtx != CNT_RX) return check_loss(tp, rxtx); off = tp->pdu_count[ANY_PDU][CNT_RX] - tp->pdu_last[ANY_PDU][CNT_RX]; break; default: /* bad offset - skip this entry */ mpoff = off = 0; break; } DEB(1, ("test_mod: kind=%d, rxtx=%d, pdukind=%d, mpoff=%d, " "whichoff=%d, off=%d\n", kind, rxtx, mod->pars.which_pdu, mpoff, mod->pars.which_offset, off)); if (!off || (mpoff != 0 && mpoff < off)) { /* This might happen in some cases. Just discard the modification. */ s = splbio(); TAILQ_REMOVE(&tp->mods, mod, link); splx(s); update_options(tp, mod); if (mod->pars.options & ISCSITEST_OPT_WAIT_FOR_COMPLETION) { mod->pars.status = ISCSI_STATUS_TEST_MODIFICATION_SKIPPED; wakeup(mod); } free(mod, M_TEMP); } } while (mpoff && mpoff < off); if (mpoff > off) return check_loss(tp, rxtx); DEB(1, ("test_mod: opt=%x, pdu_ptr=%x, num_mods=%d\n", mod->pars.options, (int) mod->pdu_ptr, mod->pars.num_pdu_mods)); if (mod->pdu_ptr) test_get(pdu, mod, err); if (mod->pars.options & ISCSITEST_OPT_DISCARD_PDU) rc = 1; else if (check_loss(tp, rxtx)) rc = 1; else if (mod->pars.num_pdu_mods) { if (!(mod->pars.options & ISCSITEST_OPT_MOD_PERMANENT)) { /* * Note: if the PDU is later resent, the unmodified one will be * used as resend_pdu restores the original io vector. */ pdu->mod_pdu = pdu->pdu; pdu->io_vec[0].iov_base = &pdu->mod_pdu; } for (i = 0; i < mod->pars.num_pdu_mods; i++) { mod_pdu(pdu, &mod->mods[i]); } } if (rxtx == CNT_TX) { if (mod->pars.options & ISCSITEST_OPT_NO_RESPONSE_PDU) { ccb_t *ccb = pdu->owner; DEB(1, ("test_mod: No response expected, completing CCB %x\n", (int)ccb)); if (ccb != NULL && (ccb->disp == CCBDISP_WAIT || ccb->disp == CCBDISP_SCSIPI)) { /* simulate timeout */ wake_ccb(ccb, ISCSI_STATUS_TIMEOUT); } } if ((mod->pars.options & ISCSITEST_SFLAG_UPDATE_FIELDS) && mod->pars.num_pdu_mods) { connection_t *conn = pdu->connection; if (conn->HeaderDigest && !(mod->pars.options & ISCSITEST_SFLAG_NO_HEADER_DIGEST)) pdu->pdu.HeaderDigest = gen_digest(&pdu->pdu, BHS_SIZE); if (pdu->uio.uio_iovcnt > 1 && conn->DataDigest && !(mod->pars.options & ISCSITEST_SFLAG_NO_DATA_DIGEST)) pdu->data_digest = gen_digest_2( pdu->io_vec[1].iov_base, pdu->io_vec[1].iov_len, pdu->io_vec[2].iov_base, pdu->io_vec[2].iov_len); } } s = splbio(); TAILQ_REMOVE(&tp->mods, mod, link); update_options(tp, mod); /* we've modified a PDU - copy current count into last count */ memcpy(tp->pdu_last, tp->pdu_count, sizeof(tp->pdu_last)); splx(s); if (mod->pars.options & ISCSITEST_OPT_WAIT_FOR_COMPLETION) { wakeup(mod); } if (mod->pars.options & ISCSITEST_KILL_CONNECTION) { kill_connection(tp->connection, ISCSI_STATUS_TEST_CONNECTION_CLOSED, NO_LOGOUT, TRUE); } free(mod, M_TEMP); return rc; }