Index: sbin/ggate/ggatec/ggatec.8 =================================================================== --- sbin/ggate/ggatec/ggatec.8 +++ sbin/ggate/ggatec/ggatec.8 @@ -33,6 +33,7 @@ .Sh SYNOPSIS .Nm .Cm create +.Op Fl 0 | Fl 1 .Op Fl n .Op Fl v .Op Fl o Cm ro | wo | rw @@ -47,6 +48,7 @@ .Ar path .Nm .Cm rescue +.Op Fl 0 | Fl 1 .Op Fl n .Op Fl v .Op Fl o Cm ro | wo | rw @@ -102,6 +104,19 @@ .Pp Available options: .Bl -tag -width ".Fl s Cm ro | wo | rw" +.It Fl 0 +Use +.Nm ggate +protocol version 0 only. +Protocol version 0 appeared in FreeBSD 5.3-RELEASE. +.It Fl 1 +Use +.Nm ggate +protocol version 1 only. +Protocol version 1 appeared in FreeBSD 13.0-RELEASE to simplify +.Nm ggate +connection negotiation. +The default is to try version 1 and then to retry with version 0. .It Fl f Forcibly destroy .Nm ggate Index: sbin/ggate/ggatec/ggatec.c =================================================================== --- sbin/ggate/ggatec/ggatec.c +++ sbin/ggate/ggatec/ggatec.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -75,14 +76,20 @@ static pthread_t sendtd, recvtd; static int reconnect; +/* bitmask of protocol versions to attempt connecting. */ +static int try_version = (1 << GGATE_VERSION_1) | (1 << GGATE_VERSION_0); + +static bool g_gatec_connect_v0(void); +static bool g_gatec_connect_v1(void); + static void usage(void) { - - fprintf(stderr, "usage: %s create [-nv] [-o ] [-p port] " + fprintf(stderr, "geom gate protocol version %d\n", G_GATE_VERSION); + fprintf(stderr, "usage: %s create [-0|-1] [-nv] [-o ] [-p port] " "[-q queue_size] [-R rcvbuf] [-S sndbuf] [-s sectorsize] " "[-t timeout] [-u unit] \n", getprogname()); - fprintf(stderr, " %s rescue [-nv] [-o ] [-p port] " + fprintf(stderr, " %s rescue [-0|-1] [-nv] [-o ] [-p port] " "[-R rcvbuf] [-S sndbuf] <-u unit> \n", getprogname()); fprintf(stderr, " %s destroy [-f] <-u unit>\n", getprogname()); fprintf(stderr, " %s list [-v] [-u unit]\n", getprogname()); @@ -283,10 +290,11 @@ /* * Create and send version packet. */ - g_gate_log(LOG_DEBUG, "Sending version packet."); assert(strlen(GGATE_MAGIC) == sizeof(ver.gv_magic)); bcopy(GGATE_MAGIC, ver.gv_magic, sizeof(ver.gv_magic)); - ver.gv_version = GGATE_VERSION; + ver.gv_version = dir ? GGATE_VERSION_0 : GGATE_VERSION_1; + g_gate_log(LOG_DEBUG, "Sending version %u packet.", + ver.gv_version); ver.gv_error = 0; g_gate_swap2n_version(&ver); if (g_gate_send(sfd, &ver, sizeof(ver), MSG_NOSIGNAL) == -1) { @@ -370,8 +378,8 @@ err(EXIT_FAILURE, "Cannot daemonize"); } -static int -g_gatec_connect(void) +static bool +g_gatec_connect_v0(void) { token = arc4random(); @@ -381,17 +389,48 @@ */ recvfd = handshake(GGATE_FLAG_SEND); if (recvfd == -1) - return (0); + return (false); /* * Our send descriptor is connected to the receive descriptor on the * server side. */ sendfd = handshake(GGATE_FLAG_RECV); if (sendfd == -1) - return (0); - return (1); + return (false); + return (true); } +static bool +g_gatec_connect_v1(void) +{ + + token = arc4random(); + recvfd = sendfd = handshake(0); + if (sendfd == -1) + return (false); + return (true); +} + +static bool +g_gatec_connect(void) +{ + + if (try_version & (1 << GGATE_VERSION_1)) { + if (g_gatec_connect_v1()) { + try_version = 1 << GGATE_VERSION_1; + return (true); + } + } + if (try_version & (1 << GGATE_VERSION_0)) { + if (g_gatec_connect_v0()) { + try_version = 1 << GGATE_VERSION_0; + return (true); + } + } + try_version = 0; + return (false); +} + static void g_gatec_start(void) { @@ -408,7 +447,8 @@ send_thread(NULL); /* Disconnected. */ close(sendfd); - close(recvfd); + if (sendfd != recvfd) + close(recvfd); } static void @@ -510,10 +550,14 @@ for (;;) { int ch; - ch = getopt(argc, argv, "fno:p:q:R:S:s:t:u:v"); + ch = getopt(argc, argv, "01fno:p:q:R:S:s:t:u:v"); if (ch == -1) break; switch (ch) { + case '0': + case '1': + try_version = (1 << (ch - '0')); + break; case 'f': if (action != DESTROY) usage(); Index: sbin/ggate/ggated/ggated.c =================================================================== --- sbin/ggate/ggated/ggated.c +++ sbin/ggate/ggated/ggated.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -64,6 +65,7 @@ #define GGATED_EXPORT_FILE "/etc/gg.exports" struct ggd_connection { + int c_version; off_t c_mediasize; unsigned c_sectorsize; unsigned c_flags; /* flags (RO/RW) */ @@ -111,6 +113,8 @@ static void *disk_thread(void *arg); static void *send_thread(void *arg); +static void connection_remove(struct ggd_connection *conn); + static void usage(void) { @@ -415,17 +419,8 @@ time(&now); LIST_FOREACH_SAFE(conn, &connections, c_next, tconn) { - if (now - conn->c_birthtime > 10) { - LIST_REMOVE(conn, c_next); - g_gate_log(LOG_NOTICE, - "Connection from %s [%s] removed.", - ip2str(conn->c_srcip), conn->c_path); - close(conn->c_diskfd); - close(conn->c_sendfd); - close(conn->c_recvfd); - free(conn->c_path); - free(conn); - } + if (now - conn->c_birthtime > 10) + connection_remove(conn); } } @@ -442,7 +437,7 @@ } static struct ggd_connection * -connection_new(struct g_gate_cinit *cinit, struct sockaddr *s, int sfd) +connection_new(int ver, struct g_gate_cinit *cinit, struct sockaddr *s, int sfd) { struct ggd_connection *conn; in_addr_t ip; @@ -458,6 +453,7 @@ conn = malloc(sizeof(*conn)); if (conn == NULL) return (NULL); + conn->c_version = ver; conn->c_path = strdup(cinit->gc_path); if (conn->c_path == NULL) { free(conn); @@ -467,15 +463,20 @@ ip = htonl(((struct sockaddr_in *)(void *)s)->sin_addr.s_addr); conn->c_srcip = ip; conn->c_diskfd = conn->c_sendfd = conn->c_recvfd = -1; - if ((cinit->gc_flags & GGATE_FLAG_SEND) != 0) - conn->c_sendfd = sfd; - else - conn->c_recvfd = sfd; conn->c_mediasize = 0; conn->c_sectorsize = 0; time(&conn->c_birthtime); conn->c_flags = cinit->gc_flags; - LIST_INSERT_HEAD(&connections, conn, c_next); + if (conn->c_version == GGATE_VERSION_1) { + conn->c_sendfd = sfd; + conn->c_recvfd = sfd; + } else if (conn->c_version == GGATE_VERSION_0) { + if ((cinit->gc_flags & GGATE_FLAG_SEND) != 0) + conn->c_sendfd = sfd; + else + conn->c_recvfd = sfd; + LIST_INSERT_HEAD(&connections, conn, c_next); + } g_gate_log(LOG_DEBUG, "Connection created [%s, %s].", ip2str(ip), conn->c_path); return (conn); @@ -518,14 +519,15 @@ connection_remove(struct ggd_connection *conn) { - LIST_REMOVE(conn, c_next); + if (conn->c_version == GGATE_VERSION_0) + LIST_REMOVE(conn, c_next); g_gate_log(LOG_DEBUG, "Connection removed [%s %s].", ip2str(conn->c_srcip), conn->c_path); if (conn->c_diskfd != -1) close(conn->c_diskfd); if (conn->c_sendfd != -1) close(conn->c_sendfd); - if (conn->c_recvfd != -1) + if (conn->c_recvfd != -1 && conn->c_sendfd != conn->c_recvfd) close(conn->c_recvfd); free(conn->c_path); free(conn); @@ -845,7 +847,11 @@ g_gate_log(LOG_INFO, "Connection from: %s.", ip2str(ip)); } -static int +/* + * handshake() returns true if sfd is closed; otherwise false so that caller + * has to close the sfd. + */ +static bool handshake(struct sockaddr *from, int sfd) { struct g_gate_version ver; @@ -864,17 +870,23 @@ g_gate_swap2h_version(&ver); if (data != sizeof(ver)) { g_gate_log(LOG_WARNING, "Malformed version packet."); - return (0); + return (false); } g_gate_log(LOG_DEBUG, "Version packet received."); if (memcmp(ver.gv_magic, GGATE_MAGIC, strlen(GGATE_MAGIC)) != 0) { g_gate_log(LOG_WARNING, "Invalid magic field."); - return (0); + return (false); } - if (ver.gv_version != GGATE_VERSION) { - g_gate_log(LOG_WARNING, "Version %u is not supported.", + switch (ver.gv_version) { + case GGATE_VERSION_0: + case GGATE_VERSION_1: + g_gate_log(LOG_DEBUG, "Received version %" PRIu16, ver.gv_version); - return (0); + break; + default: + g_gate_log(LOG_WARNING, "Version %u protocol is not supported.", + ver.gv_version); + return (false); } ver.gv_error = 0; g_gate_swap2n_version(&ver); @@ -883,7 +895,7 @@ if (data == -1) { sendfail(sfd, errno, "Error while sending version packet: %s.", strerror(errno)); - return (0); + return (false); } /* @@ -897,7 +909,18 @@ return (0); } g_gate_log(LOG_DEBUG, "Initial packet received."); - conn = connection_find(&cinit); + switch (ver.gv_version) { + case GGATE_VERSION_0: + conn = connection_find(&cinit); + break; + case GGATE_VERSION_1: + conn = NULL; + break; + default: + g_gate_log(LOG_WARNING, "Version %u is not supported.", + ver.gv_version); + return (false); + } if (conn != NULL) { /* * Connection should already exists. @@ -906,17 +929,17 @@ (unsigned long)conn->c_token); if (connection_add(conn, &cinit, from, sfd) == -1) { connection_remove(conn); - return (0); + return (false); } } else { /* * New connection, allocate space. */ - conn = connection_new(&cinit, from, sfd); + conn = connection_new(ver.gv_version, &cinit, from, sfd); if (conn == NULL) { sendfail(sfd, ENOMEM, "Cannot allocate new connection."); - return (0); + return (false); } g_gate_log(LOG_DEBUG, "New connection created (token=%lu).", (unsigned long)conn->c_token); @@ -926,7 +949,7 @@ if (ex == NULL) { sendfail(sfd, errno, NULL); connection_remove(conn); - return (0); + return (false); } if (conn->c_mediasize == 0) { conn->c_mediasize = g_gate_mediasize(conn->c_diskfd); @@ -944,14 +967,23 @@ if (data == -1) { sendfail(sfd, errno, "Error while sending initial packet: %s.", strerror(errno)); - return (0); + return (false); } - if (connection_ready(conn)) { + switch (conn->c_version) { + case GGATE_VERSION_0: + if (connection_ready(conn)) { + connection_launch(conn); + connection_remove(conn); + } + return (true); + case GGATE_VERSION_1: connection_launch(conn); connection_remove(conn); + return (true); + default: + return (false); } - return (1); } static void Index: sbin/ggate/shared/ggate.h =================================================================== --- sbin/ggate/shared/ggate.h +++ sbin/ggate/shared/ggate.h @@ -42,11 +42,25 @@ #define G_GATE_TIMEOUT 0 #define GGATE_MAGIC "GEOM_GATE " -#define GGATE_VERSION 0 +#define GGATE_VERSION_0 0 +#define GGATE_VERSION_1 1 +#define GGATE_VERSION GGATE_VERSION_1 +/* + * Protocol version 0 uses 2 TCP connections. + * ggatec initiates 2 handshakes for a single session and establishes + * 2 TCP connections. ggated waits for the 2nd handshake to complete before + * forking. + * + * Protocol version 1 uses 1 TCP connection. + * ggatec only initiates 1 handshake and ggated immediately forks. A parent + * ggated process does not cache connections for connection negotiations. + */ #define GGATE_FLAG_RDONLY 0x0001 #define GGATE_FLAG_WRONLY 0x0002 /* + * Protocol version 0 only. + * * If neither the GGATE_FLAG_SEND nor the GGATE_FLAG_RECV flag is * set - this is initial connection. * If GGATE_FLAG_SEND flag is set - this is socket to send data.