static void tb_dump_port(struct tb *tb, struct tb_regs_port_header *port) { tb_info(tb, " Port %d: %x:%x (Revision: %d, TB Version: %d, Type: %s (%#x))\n", port->port_number, port->vendor_id, port->device_id, port->revision, port->thunderbolt_version, tb_port_type(port), port->type); tb_info(tb, " Max hop id (in/out): %d/%d\n", port->max_in_hop_id, port->max_out_hop_id); tb_info(tb, " Max counters: %d\n", port->max_counters); tb_info(tb, " NFC Credits: %#x\n", port->nfc_credits); }
static void tb_dump_switch(struct tb *tb, struct tb_regs_switch_header *sw) { tb_info(tb, " Switch: %x:%x (Revision: %d, TB Version: %d)\n", sw->vendor_id, sw->device_id, sw->revision, sw->thunderbolt_version); tb_info(tb, " Max Port Number: %d\n", sw->max_port_number); tb_info(tb, " Config:\n"); tb_info(tb, " Upstream Port Number: %d Depth: %d Route String: %#llx Enabled: %d, PlugEventsDelay: %dms\n", sw->upstream_port_number, sw->depth, (((u64) sw->route_hi) << 32) | sw->route_lo, sw->enabled, sw->plug_events_delay); tb_info(tb, " unknown1: %#x unknown4: %#x\n", sw->__unknown1, sw->__unknown4); }
/** * reset_switch() - reconfigure route, enable and send TB_CFG_PKG_RESET * * Return: Returns 0 on success or an error code on failure. */ int tb_switch_reset(struct tb *tb, u64 route) { struct tb_cfg_result res; struct tb_regs_switch_header header = { header.route_hi = route >> 32, header.route_lo = route, header.enabled = true, }; tb_info(tb, "resetting switch at %llx\n", route); res.err = tb_cfg_write(tb->ctl, ((u32 *) &header) + 2, route, 0, 2, 2, 2); if (res.err) return res.err; res = tb_cfg_reset(tb->ctl, route, TB_CFG_DEFAULT_TIMEOUT); if (res.err > 0) return -EIO; return res.err; }
/** * tb_switch_alloc() - allocate and initialize a switch * * Return: Returns a NULL on failure. */ struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route) { int i; int cap; struct tb_switch *sw; int upstream_port = tb_cfg_get_upstream_port(tb->ctl, route); if (upstream_port < 0) return NULL; sw = kzalloc(sizeof(*sw), GFP_KERNEL); if (!sw) return NULL; sw->tb = tb; if (tb_cfg_read(tb->ctl, &sw->config, route, 0, TB_CFG_SWITCH, 0, 5)) goto err; tb_info(tb, "initializing Switch at %#llx (depth: %d, up port: %d)\n", route, tb_route_length(route), upstream_port); tb_info(tb, "old switch config:\n"); tb_dump_switch(tb, &sw->config); /* configure switch */ sw->config.upstream_port_number = upstream_port; sw->config.depth = tb_route_length(route); sw->config.route_lo = route; sw->config.route_hi = route >> 32; sw->config.enabled = 1; /* from here on we may use the tb_sw_* functions & macros */ if (sw->config.vendor_id != 0x8086) tb_sw_warn(sw, "unknown switch vendor id %#x\n", sw->config.vendor_id); if (sw->config.device_id != PCI_DEVICE_ID_INTEL_LIGHT_RIDGE && sw->config.device_id != PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C && sw->config.device_id != PCI_DEVICE_ID_INTEL_PORT_RIDGE && sw->config.device_id != PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_BRIDGE && sw->config.device_id != PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_BRIDGE) tb_sw_warn(sw, "unsupported switch device id %#x\n", sw->config.device_id); /* upload configuration */ if (tb_sw_write(sw, 1 + (u32 *) &sw->config, TB_CFG_SWITCH, 1, 3)) goto err; /* initialize ports */ sw->ports = kcalloc(sw->config.max_port_number + 1, sizeof(*sw->ports), GFP_KERNEL); if (!sw->ports) goto err; for (i = 0; i <= sw->config.max_port_number; i++) { /* minimum setup for tb_find_cap and tb_drom_read to work */ sw->ports[i].sw = sw; sw->ports[i].port = i; } cap = tb_find_cap(&sw->ports[0], TB_CFG_SWITCH, TB_CAP_PLUG_EVENTS); if (cap < 0) { tb_sw_warn(sw, "cannot find TB_CAP_PLUG_EVENTS aborting\n"); goto err; } sw->cap_plug_events = cap; /* read drom */ if (tb_drom_read(sw)) tb_sw_warn(sw, "tb_eeprom_read_rom failed, continuing\n"); tb_sw_info(sw, "uid: %#llx\n", sw->uid); for (i = 0; i <= sw->config.max_port_number; i++) { if (sw->ports[i].disabled) { tb_port_info(&sw->ports[i], "disabled by eeprom\n"); continue; } if (tb_init_port(&sw->ports[i])) goto err; } /* TODO: I2C, IECS, link controller */ if (tb_plug_events_active(sw, true)) goto err; return sw; err: kfree(sw->ports); kfree(sw->drom); kfree(sw); return NULL; }
/** read on a Socket_t using timeout and retries values as set in object. * \ingroup Socket * Target Socket_t must have been fully initialised by tb_Connect or tb_initServer. Read result is appended in 'msg' String_t. * * @return number of read bytes, or -1 if error. * Example: * \code * ... * String_t S = tb_String(NULL); * Socket_t So = tb_Socket(TB_TCP_UX, "/tmp/my_unix_sock", 0); * tb_Connect(So, 1, 1); * int rc; * * while(( rc = tb_readSock(So, S, 1024)) > 0); * switch( rc ) { * case -1: // error occurs * case 0: // read reached eof (or timed out) * ... * \endcode * S will contains a concatened string of all 1024's buffers read * * other Examples : * see test/srv_test.c , test/socket_test.c in build tree * * \remarks ERROR: * in case of error tb_errno will be set to : * - TB_ERR_INVALID_OBJECT : So not a TB_SOCKET * - TB_ERR_BAD : msg is not a TB_STRING * * @see tb_Socket, tb_writeSock, tb_writeSockBin, tb_readSockLine */ int tb_readSock(Socket_t S, tb_Object_t Msg, int maxlen) { fd_set set ; struct timeval tps ; int rc, retval = 0; sock_members_t So; char buff[maxlen+1]; no_error; if(! TB_VALID(S, TB_SOCKET)) { set_tb_errno(TB_ERR_INVALID_TB_OBJECT); return TB_ERR; } if(! TB_VALID(Msg, TB_STRING) && ! TB_VALID(Msg, TB_RAW)) { set_tb_errno(TB_ERR_BAD); return TB_ERR; } #ifdef WITH_XTI if( XSock(S)->addr_family == TB_X25 ) { return tb_readSock_X25(S, Msg, maxlen); } #endif So = XSock(S); if( So->buffer != NULL && tb_getSize(So->buffer) >0) { int n; n = TB_MIN( (maxlen), (tb_getSize(So->buffer))); tb_notice("readSock: already %d bytes in buffer\n", tb_getSize(So->buffer)); if( n ) { if( tb_isA(Msg) == TB_STRING) { tb_StrnAdd(Msg, n, -1, "%S", So->buffer); } else { tb_RawAdd(Msg, n, -1, S2sz(So->buffer)); } tb_StrDel(So->buffer, 0, n); if( n == maxlen) { return n; } else { maxlen -= n; retval = n; } } } restart_r_select: FD_ZERO(&set); FD_SET(So->sock, &set); tb_getSockTO(S, &(tps.tv_sec), &(tps.tv_usec)); rc = select(So->sock+1, &set, NULL, NULL, &tps); switch (rc) { case -1: if( errno == EINTR ) goto restart_r_select; tb_warn("tb_readSock[%d]: select failed (%s)\n", So->sock, strerror(errno)); // invalid fd ==> we're disconnected if( errno == EBADF ) So->status = TB_DISCONNECTED; set_tb_errno(TB_ERR_DISCONNECTED); retval = TB_ERR; break; case 0: /* Time out */ tb_notice("tb_readSock[%d]: select timed out\n", So->sock); So->status = TB_TIMEDOUT; retval = TB_KO; break ; default: if( ! FD_ISSET(So->sock, &set)) { tb_notice("tb_readSock[%d]: select rc=%d but fd is not ready\n", So->sock, rc); retval = TB_KO; break ; } restart_r_read: #ifdef WITH_SSL if( So->ssl ) { tb_info("SSL_read: try to read %d bytes\n", maxlen); rc = SSL_read(XSsl(S)->cx, buff, maxlen); switch (SSL_get_error(XSsl(S)->cx,rc)) { case SSL_ERROR_NONE: buff[rc] = 0; tb_StrAdd(Msg, -1, "%s", buff); if( rc < maxlen ) { tb_info("SSL_read: got only %d/%d\n", rc, maxlen); maxlen -= rc; goto restart_r_select; } return rc; case SSL_ERROR_SYSCALL: if( errno ) { tb_warn("tb_readSock[%d(SSL)]: read error (%s)\n", So->sock, strerror(errno)); } /* fall through */ case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_X509_LOOKUP: case SSL_ERROR_ZERO_RETURN: case SSL_ERROR_SSL: ERR_print_errors_fp(stderr); return TB_ERR; } } else { set_nonblock_flag(So->sock, 1); rc = read(So->sock, buff, maxlen); set_nonblock_flag(So->sock, 0); } #else set_nonblock_flag(So->sock, 1); rc = read(So->sock, buff, maxlen); set_nonblock_flag(So->sock, 0); #endif switch( rc ) { case -1: if( errno == EINTR ) goto restart_r_read; if( errno != EWOULDBLOCK ) { tb_error("tb_readSock[%d]: read error (%s)\n", So->sock, strerror(errno)); retval = TB_ERR; So->status = TB_DISCONNECTED; break; } case 0: // eof *buff = 0; default: retval += rc; tb_RawAdd(Msg, rc, -1, buff); break ; } break; } return retval; }
int tb_connectSSL( Socket_t S ) { BIO* sbio=NULL,* dbio; int l; sock_ssl_t m = XSsl(S); tb_info("tb_connectSSL in\n"); /* Ordinarily, just decorate the SSL connection with the socket file * descriptor. But for super-cool shazaam debugging, build your * own socket BIO, decorate it with a BIO debugging callback, and * presto, see a dump of the bytes as they fly. See ssl/ssl_lib.c * for details. */ if (tb_errorlevel <= TB_NOTICE) { SSL_set_fd(m->cx, tb_getSockFD(S)); } else { if (!(sbio=BIO_new_socket(tb_getSockFD(S),BIO_NOCLOSE))) { tb_warn("tb_connectSSL: Cannot create new socket BIO\n"); ERR_print_errors_fp(stderr); goto err; } SSL_set_bio(m->cx,sbio,sbio); m->cx->debug=1; dbio=BIO_new_fp(stdout,BIO_NOCLOSE); BIO_set_callback(sbio,(void *)bio_dump_cb); BIO_set_callback_arg(sbio,dbio); } tb_info("ssl cx linked to socket\n"); /* Initialize the state of the connection so the first i/o operation * knows to do the SSL connect. Strictly speaking, this not necessary, * as this code goes ahead and calls SSL_connect() anyway (so I can * put the connect tracing stuff in one handy spot). But look closely * in ssl/s_client.c for a call to SSL_connect. You won't find one. */ SSL_set_connect_state(m->cx); // SSL_CTX_set_session_cache_mode(XSSL(S)->ctx, SSL_SESS_CACHE_CLIENT); if(m->session != NULL ) { if(! SSL_set_session(m->cx, m->session)) { ERR_print_errors_fp(stderr); } tb_notice("tb_ConnectSSL: got a session to reuse ! (%X)\n", m->session); } /* Now that we've finally finished customizing the context and the * connection, go ahead and see if it works. This function call * invokes all the SSL connection handshake and key exchange logic, * (which is why there's so much to report on after it completes). */ retry_connect: l = SSL_connect(m->cx); tb_info("ssl connect returns: %d\n", l); switch (SSL_get_error(m->cx,l)) { case SSL_ERROR_NONE: break; case SSL_ERROR_SYSCALL: if ((l != 0) && errno) tb_warn("tb_connectSSL: Write errno=%d\n", errno); goto err; break; /* fall through */ case SSL_ERROR_WANT_WRITE: tb_info("SSL_ERROR_WANT_WRITE\n"); goto retry_connect; case SSL_ERROR_WANT_READ: tb_info("SSL_ERROR_WANT_READ\n"); goto retry_connect; case SSL_ERROR_WANT_X509_LOOKUP: tb_info("SSL_ERROR_WANT_X509_LOOKUP\n"); goto retry_connect; case SSL_ERROR_ZERO_RETURN: tb_info("SSL_ERROR_ZERO_RETURN\n"); case SSL_ERROR_SSL: tb_info("SSL_ERROR_SSL\n"); default: ERR_print_errors_fp(stderr); goto err; break; } tb_info("connected\n"); if(m->session == NULL ) { m->session = SSL_get_session(m->cx); //fixme: will leak ! tb_notice("save session for later reuse (%X)\n", m->session); } /* Report on what happened now that we've successfully connected. */ if (tb_errorlevel >= TB_NOTICE) ssl_barf_out(S); tb_info("tb_connectSSL out\n"); return TB_OK; err: tb_info("tb_connectSSL err out\n"); tb_Clear(S); return TB_ERR; }
retcode_t tb_initSSL(Socket_t S, enum ssl_mode mode, // SSL_CLIENT | SSL_SERVER ssl_meth_t method, // SSL1 | SSL2 | SSL3 | TLS1 char * CA_path, char * CA_file, char * cert, char * pwd, char * cipher) { SSL_METHOD * meth; sock_ssl_t m; tb_info("tb_initSSL in\n"); if(!TB_VALID(S, TB_SOCKET)) { set_tb_errno(TB_ERR_INVALID_TB_OBJECT); return TB_ERR; } if(XSock(S)->ssl != NULL ) { tb_warn("tb_initSSL: Socket_t allready SSL initialized\n"); set_tb_errno(TB_ERR_ALLREADY); return TB_ERR; } m = tb_xcalloc(1, sizeof(struct sock_ssl)); XSock(S)->ssl = m; m->ssl_method = method; m->mode = method; if( CA_path ) m->CA_path = tb_xstrdup(CA_path); if( CA_file ) m->CA_file = tb_xstrdup(CA_file); if( cert ) m->cert = tb_xstrdup(cert); if( pwd ) m->pwd = tb_xstrdup(pwd); if( cipher ) m->cipher = tb_xstrdup(cipher); __tb_init_SSL_once(); switch (m->ssl_method) { case 1: meth = (mode == SSL_CLIENT) ? SSLv23_client_method() : SSLv23_server_method(); break; case 2: meth = (mode == SSL_CLIENT) ? SSLv2_client_method() : SSLv2_server_method(); break; case 3: meth = (mode == SSL_CLIENT) ? SSLv3_client_method() : SSLv3_server_method(); break; case 4: meth = (mode == SSL_CLIENT) ? TLSv1_client_method() : TLSv1_server_method(); break; default: meth = NULL; goto err; } if (!(m->ctx = SSL_CTX_new(meth))) { tb_warn("tb_initSSL: Cannot create new SSL context\n"); ERR_print_errors_fp(stderr); XSock(S)->status = TB_BROKEN; return TB_ERR; } if(tb_errorlevel == TB_DEBUG) SSL_CTX_set_info_callback(m->ctx,info_cb); if(m->pwd) { SSL_CTX_set_default_passwd_cb(m->ctx, pass_cb); SSL_CTX_set_default_passwd_cb_userdata(m->ctx, S); } if(m->cert ) { if(SSL_CTX_use_certificate_file(m->ctx, m->cert, SSL_FILETYPE_PEM) <= 0) { ERR_print_errors_fp(stderr); goto err; } if (SSL_CTX_use_PrivateKey_file(m->ctx, m->cert, SSL_FILETYPE_PEM) <= 0) { tb_error("tb_initSSL: Unable to get private key from '%s'\n", m->cert); ERR_print_errors_fp(stderr); goto err; } tb_info("privkey loaded\n"); if (!SSL_CTX_check_private_key(m->ctx)) { tb_error("tb_initSSL: Private key does not match the certificate public key\n"); goto err; } tb_info("tb_initSSL: privkey validated\n"); tb_info("tb_initSSL: certificate loaded\n"); } if(mode == SSL_CLIENT) { SSL_CTX_set_session_cache_mode(m->ctx, SSL_SESS_CACHE_CLIENT); } else { SSL_CTX_set_session_cache_mode(m->ctx, SSL_SESS_CACHE_SERVER); SSL_CTX_set_session_id_context(m->ctx, "try this one", 12); } if(m->CA_file || m->CA_path) { tb_info("tb_initSSL: loading CAs ...\n"); if(!SSL_CTX_load_verify_locations(m->ctx, m->CA_file, m->CA_path)) { XSock(S)->status = TB_BROKEN; tb_warn("tb_initSSL: Cannot load verify locations %s and %s\n", m->CA_file, m->CA_path); ERR_print_errors_fp(stderr); goto err; } tb_info("tb_initSSL: CA <%s/%s> loaded\n", m->CA_path, m->CA_file); SSL_CTX_set_verify(m->ctx, SSL_VERIFY_PEER, verify_cb); SSL_CTX_set_default_verify_paths(m->ctx); } /* Create and configure SSL connection. */ if (!(m->cx = (SSL *)SSL_new(m->ctx))) { tb_warn("tb_initSSL: Cannot create new SSL context\n"); ERR_print_errors_fp(stderr); goto err; } tb_info("tb_initSSL: ssl ctx initialized\n"); /* Use OpenSSL ciphers -v to see the cipher strings and their SSL * versions, key exchange, authentication, encryption, and message * digest algorithms, and key length restrictions. See the OpenSSL * for the syntax to combine ciphers, e.g. !SSLv2:RC4-MD5:RC4-SHA. * If you don't specify anything, you get the same as "DEFAULT", which * means "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP". */ if(m->cipher) { if(!SSL_CTX_set_cipher_list(m->ctx, m->cipher)) { tb_warn("tb_inittSSL: Cannot use cipher list %s\n", m->cipher); goto err; } tb_info("tb_initSSL: cipher set to <%s>\n", m->cipher); } tb_info("tb_initSSL out\n"); return TB_OK; err: // fixme: context not totally freed (CA_path, pwd ...) SSL_CTX_free(m->ctx); m->ctx = NULL; XSock(S)->status = TB_BROKEN; return TB_ERR; }