diff --git a/server/etc/example.conf b/server/etc/example.conf index 93612e4dc..c15dc2915 100644 --- a/server/etc/example.conf +++ b/server/etc/example.conf @@ -32,30 +32,37 @@ stats = 3; # The interval in seconds to print path statistics. nodes = { udp_node = { # The dictionary is indexed by the name of the node. - type = "udp", # Type can be one of: + type = "socket", # Type can be one of: + # socket + # opal + # file + # gtfpga + + ### The following settings are specific to the socket node-type!! ### + layer = "udp" # Layer can be one of: # udp Send / recv UDP packets - # tcp Send / recv TCP packets # ip Send / recv IP packets - # ieee802.3 Send / recv raw Ethernet frames - # opal Read / write via libOpalAsyncApi - # gtfpga Read / write to memory mapped PCIe card + # eth Send / recv raw Ethernet frames (IEEE802.3) + local = "127.0.0.1:12001", # This node only received messages on this IP:Port pair remote = "127.0.0.1:12000" # This node sents outgoing messages to this IP:Port pair combine = 30 # Receive and sent 30 samples per message (multiplexing). }, - tcp_node = { - type = "tcp", # See above. + ethernet_node = { + type = "socket", # See above. + + ### The following settings are specific to the socket node-type!! ### + layer = "eth", local = "*:12002", # It's possible to use an '*' for both the port and address # in UDP / TCP and IP modes. # This will choose a random port. # And listen on all available interfaces. - remote = "127.0.0.1:12003", # Currently IPv6 is not supported! + remote = "12:34:56:78:90:12003", # Currently IPv6 is not supported! netem = { # Network emulation settings # Those settings can be specified for each node invidually! - delay = 100000, # Additional latency in microseconds jitter = 30000, # Jitter in uS distribution = "normal", # Distribution of delay: uniform, normal, pareto, paretonormal @@ -66,12 +73,16 @@ nodes = { }, opal_node = { # The server can be started as an Asynchronous process type = "opal", # from within an OPAL-RT model. + + ### The following settings are specific to the opal node-type!! ### send_id = 1, # It's possible to have multiple send / recv Icons per model recv_id = 1, # Specify the ID here. reply = true }, file_node = { type = "file", + + ### The following settings are specific to the file node-type!! ### mode = "w+", # The mode in which files should be opened (see open(2)) # You might want to use "a+" to append to a file @@ -87,6 +98,8 @@ nodes = { }, gtfpga_node = { type = "gtfpga", + + ### The following settings are specific to the gtfpga node-type!! ### slot = "01:00.0", # The PCIe slot location (see first column in 'lspci' output) id = "1ab8:4005", # The PCIe vendor:device ID (see third column in 'lspci -n' output) diff --git a/server/include/node.h b/server/include/node.h index 382597dbf..86509322c 100644 --- a/server/include/node.h +++ b/server/include/node.h @@ -41,16 +41,10 @@ /** Node type: layer, protocol, listen/connect */ enum node_type { + BSD_SOCKET, /**< BSD Socket API */ LOG_FILE, /**< File IO */ OPAL_ASYNC, /**< OPAL-RT Asynchronous Process Api */ - GTFPGA, /**< Xilinx ML507 GTFPGA card */ - IEEE_802_3 = 0x10, /**< BSD socket: AF_PACKET SOCK_DGRAM */ - IP, /**< BSD socket: AF_INET SOCK_RAW */ - UDP, /**< BSD socket: AF_INET SOCK_DGRAM */ - TCPD, /**< BSD socket: AF_INET SOCK_STREAM bind + listen + accept */ - TCP, /**< BSD socket: AF_INET SOCK_STREAM bind + connect */ - - SOCKET = 0xF0 /**< Mask for BSD socket types */ + GTFPGA /**< Xilinx ML507 GTFPGA card */ }; /** C++ like vtable construct for node_types diff --git a/server/include/socket.h b/server/include/socket.h index 0580764b4..718b9ba69 100644 --- a/server/include/socket.h +++ b/server/include/socket.h @@ -19,14 +19,21 @@ #include "node.h" +enum socket_layer { + LAYER_ETH, + LAYER_IP, + LAYER_UDP +}; + struct socket { /** The socket descriptor */ int sd; - /** The socket descriptor for an established TCP connection */ - int sd2; /** Socket mark for netem, routing and filtering */ int mark; + /** The OSI / IP layer which should be used for this socket */ + enum socket_layer layer; + /** Local address of the socket */ struct sockaddr_storage local; /** Remote address of the socket */ @@ -85,11 +92,11 @@ int socket_print_addr(char *buf, int len, struct sockaddr *sa); * * @param str A string specifiying the socket address. See description for allowed formats. * @param sa A pointer to the resolved address - * @param type Specifies the address type in which the addr is given + * @param layer Specifies the address type in which the addr is given * @param flags Flags for getaddrinfo(2) * @retval 0 Success. Everything went well. * @retval <0 Error. Something went wrong. */ -int socket_parse_addr(const char *str, struct sockaddr *sa, enum node_type type, int flags); +int socket_parse_addr(const char *str, struct sockaddr *sa, enum socket_layer layer, int flags); #endif /** _SOCKET_H_ @} */ diff --git a/server/src/node.c b/server/src/node.c index ae72caae1..73ad319e1 100644 --- a/server/src/node.c +++ b/server/src/node.c @@ -36,10 +36,8 @@ struct node_vtable vtables[] = { #ifdef ENABLE_GTFPGA VTABLE(GTFPGA, "gtfpga", gtfpga), #endif - VTABLE(LOG_FILE, "file", file), - VTABLE(IEEE_802_3, "ieee802.3", socket), - VTABLE(IP, "ip", socket), - VTABLE(UDP, "udp", socket) + VTABLE(BSD_SOCKET, "socket", socket), + VTABLE(LOG_FILE, "file", file) }; int node_init(int argc, char *argv[], struct settings *set) @@ -120,9 +118,7 @@ int node_stop(struct node *n) void node_reverse(struct node *n) { switch (n->vt->type) { - case IEEE_802_3: - case IP: - case UDP: + case BSD_SOCKET: SWAP(n->socket->remote, n->socket->local); break; default: { } @@ -137,10 +133,9 @@ struct node * node_create() void node_destroy(struct node *n) { switch (n->vt->type) { - case IEEE_802_3: - case IP: - case UDP: + case BSD_SOCKET: free(n->socket->netem); + break; default: { } } diff --git a/server/src/socket.c b/server/src/socket.c index d2665707f..e7c311614 100644 --- a/server/src/socket.c +++ b/server/src/socket.c @@ -98,10 +98,10 @@ int socket_open(struct node *n) int ret; /* Create socket */ - switch (node_type(n)) { - case UDP: s->sd = socket(sin->sin_family, SOCK_DGRAM, IPPROTO_UDP); break; - case IP: s->sd = socket(sin->sin_family, SOCK_RAW, ntohs(sin->sin_port)); break; - case IEEE_802_3:s->sd = socket(sll->sll_family, SOCK_DGRAM, sll->sll_protocol); break; + switch (s->layer) { + case LAYER_UDP: s->sd = socket(sin->sin_family, SOCK_DGRAM, IPPROTO_UDP); break; + case LAYER_IP: s->sd = socket(sin->sin_family, SOCK_RAW, ntohs(sin->sin_port)); break; + case LAYER_ETH: s->sd = socket(sll->sll_family, SOCK_DGRAM, sll->sll_protocol); break; default: error("Invalid socket type!"); } @@ -122,9 +122,9 @@ int socket_open(struct node *n) /* Set socket priority, QoS or TOS IP options */ int prio; - switch (node_type(n)) { - case UDP: - case IP: + switch (s->layer) { + case LAYER_UDP: + case LAYER_IP: prio = IPTOS_LOWDELAY; if (setsockopt(s->sd, IPPROTO_IP, IP_TOS, &prio, sizeof(prio))) serror("Failed to set type of service (QoS)"); @@ -150,10 +150,7 @@ int socket_close(struct node *n) if (s->sd >= 0) close(s->sd); - - if (s->sd2 >= 0) - close(s->sd2); - + return 0; } @@ -216,62 +213,77 @@ int socket_read(struct node *n, struct msg *pool, int poolsize, int first, int c int socket_write(struct node *n, struct msg *pool, int poolsize, int first, int cnt) { struct socket *s = n->socket; - int ret = -1; - + int ret = -1, sent = 0; + struct iovec iov[cnt]; - struct msghdr mhdr = { - .msg_iov = iov, - .msg_iovlen = ARRAY_LEN(iov) - }; - for (int i = 0; i < cnt; i++) { - struct msg *n = &pool[(first+poolsize+i) % poolsize]; - - /* Convert headers to host byte order */ + struct msg *n = &pool[(first+i) % poolsize]; + + if (n->type == MSG_TYPE_EMPTY) + continue; + + /* Convert headers to network byte order */ n->sequence = htons(n->sequence); - - iov[i].iov_base = n; - iov[i].iov_len = MSG_LEN(n); + + iov[sent].iov_base = n; + iov[sent].iov_len = MSG_LEN(n); + + sent++; } + struct msghdr mhdr = { + .msg_iov = iov, + .msg_iovlen = sent + }; + /* Specify destination address for connection-less procotols */ - switch (node_type(n)) { - case IEEE_802_3: - case IP: - case UDP: + switch (s->layer) { + case LAYER_UDP: + case LAYER_IP: + case LAYER_ETH: mhdr.msg_name = (struct sockaddr *) &s->remote; mhdr.msg_namelen = sizeof(s->remote); break; - default: - break; } ret = sendmsg(s->sd, &mhdr, 0); if (ret < 0) serror("Failed send"); - return cnt; + return sent; } int socket_parse(config_setting_t *cfg, struct node *n) { - const char *local, *remote; + const char *local, *remote, *layer; int ret; struct socket *s = alloc(sizeof(struct socket)); + if (!config_setting_lookup_string(cfg, "layer", &layer)) + cerror(cfg, "Missing layer setting for node '%s'", n->name); + + if (!strcmp(layer, "eth")) + s->layer = LAYER_ETH; + else if (!strcmp(layer, "ip")) + s->layer = LAYER_IP; + else if (!strcmp(layer, "udp")) + s->layer = LAYER_UDP; + else + cerror(cfg, "Invalid layer '%s' for node '%s'", layer, n->name); + if (!config_setting_lookup_string(cfg, "remote", &remote)) cerror(cfg, "Missing remote address for node '%s'", n->name); if (!config_setting_lookup_string(cfg, "local", &local)) cerror(cfg, "Missing local address for node '%s'", n->name); - ret = socket_parse_addr(local, (struct sockaddr *) &s->local, node_type(n), AI_PASSIVE); + ret = socket_parse_addr(local, (struct sockaddr *) &s->local, s->layer, AI_PASSIVE); if (ret) cerror(cfg, "Failed to resolve local address '%s' of node '%s': %s", local, n->name, gai_strerror(ret)); - ret = socket_parse_addr(remote, (struct sockaddr *) &s->remote, node_type(n), 0); + ret = socket_parse_addr(remote, (struct sockaddr *) &s->remote, s->layer, 0); if (ret) cerror(cfg, "Failed to resolve remote address '%s' of node '%s': %s", remote, n->name, gai_strerror(ret)); @@ -317,7 +329,7 @@ int socket_print_addr(char *buf, int len, struct sockaddr *sa) return 0; } -int socket_parse_addr(const char *addr, struct sockaddr *sa, enum node_type type, int flags) +int socket_parse_addr(const char *addr, struct sockaddr *sa, enum socket_layer layer, int flags) { /** @todo: Add support for IPv6 */ @@ -362,14 +374,14 @@ int socket_parse_addr(const char *addr, struct sockaddr *sa, enum node_type type if (service && !strcmp(service, "*")) service = NULL; - switch (type) { - case IP: + switch (layer) { + case LAYER_IP: hint.ai_socktype = SOCK_RAW; hint.ai_protocol = (service) ? strtol(service, NULL, 0) : IPPROTO_S2SS; hint.ai_flags |= AI_NUMERICSERV; break; - case UDP: + case LAYER_UDP: hint.ai_socktype = SOCK_DGRAM; hint.ai_protocol = IPPROTO_UDP; break; @@ -380,10 +392,10 @@ int socket_parse_addr(const char *addr, struct sockaddr *sa, enum node_type type /* Lookup address */ struct addrinfo *result; - ret = getaddrinfo(node, (type == IP) ? NULL : service, &hint, &result); + ret = getaddrinfo(node, (layer == LAYER_IP) ? NULL : service, &hint, &result); if (!ret) { - - if (type == IP) { + + if (layer == LAYER_IP) { /* We mis-use the sin_port field to store the IP protocol number on RAW sockets */ struct sockaddr_in *sin = (struct sockaddr_in *) result->ai_addr; sin->sin_port = htons(result->ai_protocol);