int zmq::pgm_socket_t::open_transport () { // Can not open transport before destroying old one. zmq_assert (transport == NULL); // Zero counter used in msgrecv. nbytes_rec = 0; nbytes_processed = 0; pgm_msgv_processed = 0; // TODO: Converting bool to int? Not nice. int pgm_ok = true; GError *pgm_error = NULL; // Init PGM transport. // Ensure threading enabled, ensure timer enabled and find PGM protocol id. // // Note that if you want to use gettimeofday and sleep for openPGM timing, // set environment variables PGM_TIMER to "GTOD" // and PGM_SLEEP to "USLEEP". int rc = pgm_init (); if (rc != 0) { errno = EINVAL; return -1; } // PGM transport GSI. pgm_gsi_t gsi; std::string gsi_base; if (options.identity.size () > 0) { // Create gsi from identity string. gsi_base = options.identity; } else { // Generate random gsi. gsi_base = uuid_t ().to_string (); } rc = pgm_gsi_create_from_string (&gsi, gsi_base.c_str (), -1); if (rc != pgm_ok) { errno = EINVAL; return -1; } //zmq_log (1, "Transport GSI: %s, %s(%i)\n", pgm_print_gsi (&gsi), // __FILE__, __LINE__); struct pgm_transport_info_t *res = NULL; if (!pgm_if_get_transport_info (network, NULL, &res, &pgm_error)) { errno = EINVAL; return -1; } res->ti_gsi = gsi; res->ti_dport = port_number; // If we are using UDP encapsulation update gsr or res. if (udp_encapsulation) { res->ti_udp_encap_ucast_port = port_number; res->ti_udp_encap_mcast_port = port_number; } if (!pgm_transport_create (&transport, res, &pgm_error)) { pgm_if_free_transport_info (res); // TODO: tranlate errors from glib into errnos. errno = EINVAL; return -1; } pgm_if_free_transport_info (res); // Common parameters for receiver and sender. // Set maximum transport protocol data unit size (TPDU). rc = pgm_transport_set_max_tpdu (transport, pgm_max_tpdu); if (rc != pgm_ok) { errno = EINVAL; return -1; } // Set maximum number of network hops to cross. rc = pgm_transport_set_hops (transport, 16); if (rc != pgm_ok) { errno = EINVAL; return -1; } // Set nonblocking send/recv sockets. if (!pgm_transport_set_nonblocking (transport, true)) { errno = EINVAL; return -1; } if (receiver) { // Receiver transport. // Set transport->can_send_data = FALSE. // Note that NAKs are still generated by the transport. rc = pgm_transport_set_recv_only (transport, true, false); zmq_assert (rc == pgm_ok); if (options.rcvbuf) { rc = pgm_transport_set_rcvbuf (transport, (int) options.rcvbuf); if (rc != pgm_ok) return -1; } // Set NAK transmit back-off interval [us]. rc = pgm_transport_set_nak_bo_ivl (transport, 50 * 1000); zmq_assert (rc == pgm_ok); // Set timeout before repeating NAK [us]. rc = pgm_transport_set_nak_rpt_ivl (transport, 200 * 1000); zmq_assert (rc == pgm_ok); // Set timeout for receiving RDATA. rc = pgm_transport_set_nak_rdata_ivl (transport, 200 * 1000); zmq_assert (rc == pgm_ok); // Set retries for NAK without NCF/DATA (NAK_DATA_RETRIES). rc = pgm_transport_set_nak_data_retries (transport, 5); zmq_assert (rc == pgm_ok); // Set retries for NCF after NAK (NAK_NCF_RETRIES). rc = pgm_transport_set_nak_ncf_retries (transport, 2); zmq_assert (rc == pgm_ok); // Set timeout for removing a dead peer [us]. rc = pgm_transport_set_peer_expiry (transport, 5 * 8192 * 1000); zmq_assert (rc == pgm_ok); // Set expiration time of SPM Requests [us]. rc = pgm_transport_set_spmr_expiry (transport, 25 * 1000); zmq_assert (rc == pgm_ok); // Set the size of the receive window. // Data rate is in [B/s]. options.rate is in [kb/s]. if (options.rate <= 0) { errno = EINVAL; return -1; } rc = pgm_transport_set_rxw_max_rte (transport, options.rate * 1000 / 8); if (rc != pgm_ok) { errno = EINVAL; return -1; } // Recovery interval [s]. if (options.recovery_ivl <= 0) { errno = EINVAL; return -1; } rc = pgm_transport_set_rxw_secs (transport, options.recovery_ivl); if (rc != pgm_ok) { errno = EINVAL; return -1; } } else { // Sender transport. // Set transport->can_recv = FALSE, waiting_pipe will not be read. rc = pgm_transport_set_send_only (transport, TRUE); zmq_assert (rc == pgm_ok); if (options.sndbuf) { rc = pgm_transport_set_sndbuf (transport, (int) options.sndbuf); if (rc != pgm_ok) return -1; } // Set the size of the send window. // Data rate is in [B/s] options.rate is in [kb/s]. if (options.rate <= 0) { errno = EINVAL; return -1; } rc = pgm_transport_set_txw_max_rte (transport, options.rate * 1000 / 8); if (rc != pgm_ok) { errno = EINVAL; return -1; } // Recovery interval [s]. if (options.recovery_ivl <= 0) { errno = EINVAL; return -1; } rc = pgm_transport_set_txw_secs (transport, options.recovery_ivl); if (rc != pgm_ok) { errno = EINVAL; return -1; } // Set interval of background SPM packets [us]. rc = pgm_transport_set_ambient_spm (transport, 8192 * 1000); zmq_assert (rc == pgm_ok); // Set intervals of data flushing SPM packets [us]. guint spm_heartbeat[] = {4 * 1000, 4 * 1000, 8 * 1000, 16 * 1000, 32 * 1000, 64 * 1000, 128 * 1000, 256 * 1000, 512 * 1000, 1024 * 1000, 2048 * 1000, 4096 * 1000, 8192 * 1000}; rc = pgm_transport_set_heartbeat_spm (transport, spm_heartbeat, G_N_ELEMENTS(spm_heartbeat)); zmq_assert (rc == pgm_ok); } // Enable multicast loopback. if (options.use_multicast_loop) { rc = pgm_transport_set_multicast_loop (transport, true); zmq_assert (rc == pgm_ok); } // Bind a transport to the specified network devices. if (!pgm_transport_bind (transport, &pgm_error)) { // TODO: tranlate errors from glib into errnos. return -1; } return 0; }
int zmq::pgm_socket_t::init (bool udp_encapsulation_, const char *network_) { // Can not open transport before destroying old one. zmq_assert (transport == NULL); // Parse port number. const char *port_delim = strchr (network_, ':'); if (!port_delim) { errno = EINVAL; return -1; } uint16_t port_number = atoi (port_delim + 1); char network [256]; if (port_delim - network_ >= (int) sizeof (network) - 1) { errno = EINVAL; return -1; } memset (network, '\0', sizeof (network)); memcpy (network, network_, port_delim - network_); // Zero counter used in msgrecv. nbytes_rec = 0; nbytes_processed = 0; pgm_msgv_processed = 0; GError *pgm_error = NULL; // Init PGM transport. // Ensure threading enabled, ensure timer enabled and find PGM protocol id. // // Note that if you want to use gettimeofday and sleep for openPGM timing, // set environment variables PGM_TIMER to "GTOD" // and PGM_SLEEP to "USLEEP". int rc = pgm_init (); if (rc != 0) { errno = EINVAL; return -1; } // PGM transport GSI. pgm_gsi_t gsi; std::string gsi_base; if (options.identity.size () > 0) { // Create gsi from identity string. gsi_base = options.identity; } else { // Generate random gsi. gsi_base = uuid_t ().to_string (); } rc = pgm_gsi_create_from_string (&gsi, gsi_base.c_str (), -1); if (rc != TRUE) { errno = EINVAL; return -1; } struct pgm_transport_info_t *res = NULL; if (!pgm_if_get_transport_info (network, NULL, &res, &pgm_error)) { if (pgm_error->domain == PGM_IF_ERROR && ( pgm_error->code == PGM_IF_ERROR_INVAL || pgm_error->code == PGM_IF_ERROR_XDEV || pgm_error->code == PGM_IF_ERROR_NODEV || pgm_error->code == PGM_IF_ERROR_NOTUNIQ || pgm_error->code == PGM_IF_ERROR_ADDRFAMILY || pgm_error->code == PGM_IF_ERROR_FAMILY || pgm_error->code == PGM_IF_ERROR_NODATA || pgm_error->code == PGM_IF_ERROR_NONAME || pgm_error->code == PGM_IF_ERROR_SERVICE)) { errno = EINVAL; g_error_free (pgm_error); return -1; } zmq_assert (false); } res->ti_gsi = gsi; res->ti_dport = port_number; // If we are using UDP encapsulation update gsr or res. if (udp_encapsulation_) { res->ti_udp_encap_ucast_port = port_number; res->ti_udp_encap_mcast_port = port_number; } if (!pgm_transport_create (&transport, res, &pgm_error)) { if (pgm_error->domain == PGM_TRANSPORT_ERROR && ( pgm_error->code == PGM_TRANSPORT_ERROR_INVAL || pgm_error->code == PGM_TRANSPORT_ERROR_PERM || pgm_error->code == PGM_TRANSPORT_ERROR_NODEV)) { pgm_if_free_transport_info (res); g_error_free (pgm_error); errno = EINVAL; return -1; } zmq_assert (false); } pgm_if_free_transport_info (res); // Common parameters for receiver and sender. // Set maximum transport protocol data unit size (TPDU). rc = pgm_transport_set_max_tpdu (transport, pgm_max_tpdu); if (rc != TRUE) { errno = EINVAL; return -1; } // Set maximum number of network hops to cross. rc = pgm_transport_set_hops (transport, 16); if (rc != TRUE) { errno = EINVAL; return -1; } // Set nonblocking send/recv sockets. if (!pgm_transport_set_nonblocking (transport, true)) { errno = EINVAL; return -1; } if (receiver) { // Receiver transport. // Note that NAKs are still generated by the transport. rc = pgm_transport_set_recv_only (transport, true, false); zmq_assert (rc == TRUE); if (options.rcvbuf) { rc = pgm_transport_set_rcvbuf (transport, (int) options.rcvbuf); if (rc != TRUE) return -1; } // Set NAK transmit back-off interval [us]. rc = pgm_transport_set_nak_bo_ivl (transport, 50 * 1000); zmq_assert (rc == TRUE); // Set timeout before repeating NAK [us]. rc = pgm_transport_set_nak_rpt_ivl (transport, 200 * 1000); zmq_assert (rc == TRUE); // Set timeout for receiving RDATA. rc = pgm_transport_set_nak_rdata_ivl (transport, 200 * 1000); zmq_assert (rc == TRUE); // Set retries for NAK without NCF/DATA (NAK_DATA_RETRIES). rc = pgm_transport_set_nak_data_retries (transport, 5); zmq_assert (rc == TRUE); // Set retries for NCF after NAK (NAK_NCF_RETRIES). rc = pgm_transport_set_nak_ncf_retries (transport, 2); zmq_assert (rc == TRUE); // Set timeout for removing a dead peer [us]. rc = pgm_transport_set_peer_expiry (transport, 5 * 8192 * 1000); zmq_assert (rc == TRUE); // Set expiration time of SPM Requests [us]. rc = pgm_transport_set_spmr_expiry (transport, 25 * 1000); zmq_assert (rc == TRUE); // Set the size of the receive window. // Data rate is in [B/s]. options.rate is in [kb/s]. if (options.rate <= 0) { errno = EINVAL; return -1; } rc = pgm_transport_set_rxw_max_rte (transport, options.rate * 1000 / 8); if (rc != TRUE) { errno = EINVAL; return -1; } // Recovery interval [s]. if (options.recovery_ivl <= 0) { errno = EINVAL; return -1; } rc = pgm_transport_set_rxw_secs (transport, options.recovery_ivl); if (rc != TRUE) { errno = EINVAL; return -1; } } else { // Sender transport. // Waiting pipe won't be read. rc = pgm_transport_set_send_only (transport, TRUE); zmq_assert (rc == TRUE); if (options.sndbuf) { rc = pgm_transport_set_sndbuf (transport, (int) options.sndbuf); if (rc != TRUE) return -1; } // Set the size of the send window. // Data rate is in [B/s] options.rate is in [kb/s]. if (options.rate <= 0) { errno = EINVAL; return -1; } rc = pgm_transport_set_txw_max_rte (transport, options.rate * 1000 / 8); if (rc != TRUE) { errno = EINVAL; return -1; } // Recovery interval [s]. if (options.recovery_ivl <= 0) { errno = EINVAL; return -1; } rc = pgm_transport_set_txw_secs (transport, options.recovery_ivl); if (rc != TRUE) { errno = EINVAL; return -1; } // Set interval of background SPM packets [us]. rc = pgm_transport_set_ambient_spm (transport, 8192 * 1000); zmq_assert (rc == TRUE); // Set intervals of data flushing SPM packets [us]. guint spm_heartbeat[] = {4 * 1000, 4 * 1000, 8 * 1000, 16 * 1000, 32 * 1000, 64 * 1000, 128 * 1000, 256 * 1000, 512 * 1000, 1024 * 1000, 2048 * 1000, 4096 * 1000, 8192 * 1000}; rc = pgm_transport_set_heartbeat_spm (transport, spm_heartbeat, G_N_ELEMENTS(spm_heartbeat)); zmq_assert (rc == TRUE); } // Enable multicast loopback. if (options.use_multicast_loop) { rc = pgm_transport_set_multicast_loop (transport, true); zmq_assert (rc == TRUE); } // Bind a transport to the specified network devices. if (!pgm_transport_bind (transport, &pgm_error)) { if (pgm_error->domain == PGM_IF_ERROR && ( pgm_error->code == PGM_IF_ERROR_INVAL || pgm_error->code == PGM_IF_ERROR_XDEV || pgm_error->code == PGM_IF_ERROR_NODEV || pgm_error->code == PGM_IF_ERROR_NOTUNIQ || pgm_error->code == PGM_IF_ERROR_ADDRFAMILY || pgm_error->code == PGM_IF_ERROR_FAMILY || pgm_error->code == PGM_IF_ERROR_NODATA || pgm_error->code == PGM_IF_ERROR_NONAME || pgm_error->code == PGM_IF_ERROR_SERVICE)) { g_error_free (pgm_error); errno = EINVAL; return -1; } zmq_assert (false); } // For receiver transport preallocate pgm_msgv array. // TODO: ? if (receiver) { zmq_assert (in_batch_size > 0); size_t max_tsdu_size = get_max_tsdu_size (); pgm_msgv_len = (int) in_batch_size / max_tsdu_size; if ((int) in_batch_size % max_tsdu_size) pgm_msgv_len++; zmq_assert (pgm_msgv_len); pgm_msgv = (pgm_msgv_t*) malloc (sizeof (pgm_msgv_t) * pgm_msgv_len); } return 0; }
// Create, bind and connect PGM socket. // network_ of the form <interface & multicast group decls>:<IP port> // e.g. eth0;239.192.0.1:7500 // link-local;224.250.0.1,224.250.0.2;224.250.0.3:8000 // ;[fe80::1%en0]:7500 int zmq::pgm_socket_t::init (bool udp_encapsulation_, const char *network_) { // Can not open transport before destroying old one. zmq_assert (sock == NULL); // Parse port number, start from end for IPv6 const char *port_delim = strrchr (network_, ':'); if (!port_delim) { errno = EINVAL; return -1; } uint16_t port_number = atoi (port_delim + 1); char network [256]; if (port_delim - network_ >= (int) sizeof (network) - 1) { errno = EINVAL; return -1; } memset (network, '\0', sizeof (network)); memcpy (network, network_, port_delim - network_); // Validate socket options // Data rate is in [B/s]. options.rate is in [kb/s]. if (options.rate <= 0) { errno = EINVAL; return -1; } // Recovery interval [s]. if (options.recovery_ivl <= 0) { errno = EINVAL; return -1; } // Zero counter used in msgrecv. nbytes_rec = 0; nbytes_processed = 0; pgm_msgv_processed = 0; bool rc; pgm_error_t *pgm_error = NULL; struct pgm_addrinfo_t hints, *res = NULL; sa_family_t sa_family; memset (&hints, 0, sizeof (hints)); hints.ai_family = AF_UNSPEC; if (!pgm_getaddrinfo (network, NULL, &res, &pgm_error)) { // Invalid parameters don't set pgm_error_t zmq_assert (pgm_error != NULL); if (pgm_error->domain == PGM_ERROR_DOMAIN_IF && ( // NB: cannot catch EAI_BADFLAGS pgm_error->code != PGM_ERROR_SERVICE && pgm_error->code != PGM_ERROR_SOCKTNOSUPPORT)) // User, host, or network configuration or transient error goto err_abort; // Fatal OpenPGM internal error zmq_assert (false); } zmq_assert (res != NULL); // Pick up detected IP family sa_family = res->ai_send_addrs[0].gsr_group.ss_family; // Create IP/PGM or UDP/PGM socket if (udp_encapsulation_) { if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_error)) { // Invalid parameters don't set pgm_error_t zmq_assert (pgm_error != NULL); if (pgm_error->domain == PGM_ERROR_DOMAIN_SOCKET && ( pgm_error->code != PGM_ERROR_BADF && pgm_error->code != PGM_ERROR_FAULT && pgm_error->code != PGM_ERROR_NOPROTOOPT && pgm_error->code != PGM_ERROR_FAILED)) // User, host, or network configuration or transient error goto err_abort; // Fatal OpenPGM internal error zmq_assert (false); } // All options are of data type int const int encapsulation_port = port_number; if (!pgm_setsockopt (sock, IPPROTO_PGM, PGM_UDP_ENCAP_UCAST_PORT, &encapsulation_port, sizeof (encapsulation_port)) || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_UDP_ENCAP_MCAST_PORT, &encapsulation_port, sizeof (encapsulation_port))) goto err_abort; } else { if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_error)) { // Invalid parameters don't set pgm_error_t zmq_assert (pgm_error != NULL); if (pgm_error->domain == PGM_ERROR_DOMAIN_SOCKET && ( pgm_error->code != PGM_ERROR_BADF && pgm_error->code != PGM_ERROR_FAULT && pgm_error->code != PGM_ERROR_NOPROTOOPT && pgm_error->code != PGM_ERROR_FAILED)) // User, host, or network configuration or transient error goto err_abort; // Fatal OpenPGM internal error zmq_assert (false); } } { const int rcvbuf = (int) options.rcvbuf, sndbuf = (int) options.sndbuf, max_tpdu = (int) pgm_max_tpdu; if (rcvbuf) { if (!pgm_setsockopt (sock, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof (rcvbuf))) goto err_abort; } if (sndbuf) { if (!pgm_setsockopt (sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof (sndbuf))) goto err_abort; } // Set maximum transport protocol data unit size (TPDU). if (!pgm_setsockopt (sock, IPPROTO_PGM, PGM_MTU, &max_tpdu, sizeof (max_tpdu))) goto err_abort; } if (receiver) { const int recv_only = 1, rxw_max_rte = options.rate * 1000 / 8, rxw_secs = options.recovery_ivl, peer_expiry = 5 * pgm_msecs (8192), spmr_expiry = pgm_msecs (25), nak_bo_ivl = pgm_msecs (50), nak_rpt_ivl = pgm_msecs (200), nak_rdata_ivl = pgm_msecs (200), nak_data_retries = 5, nak_ncf_retries = 2; if (!pgm_setsockopt (sock, IPPROTO_PGM, PGM_RECV_ONLY, &recv_only, sizeof (recv_only)) || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_RXW_MAX_RTE, &rxw_max_rte, sizeof (rxw_max_rte)) || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_RXW_SECS, &rxw_secs, sizeof (rxw_secs)) || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_PEER_EXPIRY, &peer_expiry, sizeof (peer_expiry)) || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof (spmr_expiry)) || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof (nak_bo_ivl)) || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof (nak_rpt_ivl)) || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof (nak_rdata_ivl)) || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof (nak_data_retries)) || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof (nak_ncf_retries))) goto err_abort; } else { const int send_only = 1, txw_max_rte = options.rate * 1000 / 8, txw_secs = options.recovery_ivl, ambient_spm = pgm_msecs (8192), heartbeat_spm[] = { pgm_msecs (4), pgm_msecs (4), pgm_msecs (8), pgm_msecs (16), pgm_msecs (32), pgm_msecs (64), pgm_msecs (128), pgm_msecs (256), pgm_msecs (512), pgm_msecs (1024), pgm_msecs (2048), pgm_msecs (4096), pgm_msecs (8192) }; if (!pgm_setsockopt (sock, IPPROTO_PGM, PGM_SEND_ONLY, &send_only, sizeof (send_only)) || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_TXW_MAX_RTE, &txw_max_rte, sizeof (txw_max_rte)) || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_TXW_SECS, &txw_secs, sizeof (txw_secs)) || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_AMBIENT_SPM, &ambient_spm, sizeof (ambient_spm)) || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_HEARTBEAT_SPM, &heartbeat_spm, sizeof (heartbeat_spm))) goto err_abort; } // PGM transport GSI. struct pgm_sockaddr_t addr; memset (&addr, 0, sizeof(addr)); addr.sa_port = port_number; addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; if (options.identity.size () > 0) { // Create gsi from identity. if (!pgm_gsi_create_from_data (&addr.sa_addr.gsi, options.identity.data (), options.identity.size ())) goto err_abort; } else { // Generate random gsi. std::string gsi_base = uuid_t ().to_string (); if (!pgm_gsi_create_from_string (&addr.sa_addr.gsi, gsi_base.c_str (), -1)) goto err_abort; } // Bind a transport to the specified network devices. struct pgm_interface_req_t if_req; memset (&if_req, 0, sizeof(if_req)); if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; if_req.ir_scope_id = 0; if (AF_INET6 == sa_family) { struct sockaddr_in6 sa6; memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof (sa6)); if_req.ir_scope_id = sa6.sin6_scope_id; } if (!pgm_bind3 (sock, &addr, sizeof (addr), &if_req, sizeof (if_req), &if_req, sizeof (if_req), &pgm_error)) { // Invalid parameters don't set pgm_error_t zmq_assert (pgm_error != NULL); if ((pgm_error->domain == PGM_ERROR_DOMAIN_SOCKET || pgm_error->domain == PGM_ERROR_DOMAIN_IF) && ( pgm_error->code != PGM_ERROR_INVAL && pgm_error->code != PGM_ERROR_BADF && pgm_error->code != PGM_ERROR_FAULT)) // User, host, or network configuration or transient error goto err_abort; // Fatal OpenPGM internal error zmq_assert (false); } // Join IP multicast groups for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) { if (!pgm_setsockopt (sock, IPPROTO_PGM, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof (struct group_req))) goto err_abort; } if (!pgm_setsockopt (sock, IPPROTO_PGM, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof (struct group_req))) goto err_abort; pgm_freeaddrinfo (res); res = NULL; // Set IP level parameters { const int nonblocking = 1, multicast_loop = options.use_multicast_loop ? 1 : 0, multicast_hops = 16, dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ if (!pgm_setsockopt (sock, IPPROTO_PGM, PGM_MULTICAST_LOOP, &multicast_loop, sizeof (multicast_loop)) || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_MULTICAST_HOPS, &multicast_hops, sizeof (multicast_hops))) goto err_abort; if (AF_INET6 != sa_family && !pgm_setsockopt (sock, IPPROTO_PGM, PGM_TOS, &dscp, sizeof (dscp))) goto err_abort; if (!pgm_setsockopt (sock, IPPROTO_PGM, PGM_NOBLOCK, &nonblocking, sizeof (nonblocking))) goto err_abort; } // Connect PGM transport to start state machine. if (!pgm_connect (sock, &pgm_error)) { // Invalid parameters don't set pgm_error_t zmq_assert (pgm_error != NULL); goto err_abort; } // For receiver transport preallocate pgm_msgv array. if (receiver) { zmq_assert (in_batch_size > 0); size_t max_tsdu_size = get_max_tsdu_size (); pgm_msgv_len = (int) in_batch_size / max_tsdu_size; if ((int) in_batch_size % max_tsdu_size) pgm_msgv_len++; zmq_assert (pgm_msgv_len); pgm_msgv = (pgm_msgv_t*) malloc (sizeof (pgm_msgv_t) * pgm_msgv_len); } return 0; err_abort: if (sock != NULL) { pgm_close (sock, FALSE); sock = NULL; } if (res != NULL) { pgm_freeaddrinfo (res); res = NULL; } if (pgm_error != NULL) { pgm_error_free (pgm_error); pgm_error = NULL; } errno = EINVAL; return -1; }