Add outgoing SCTP connect support

This commit is contained in:
Simon Arlott 2017-08-22 20:59:53 +01:00
parent c6ad9b0c5f
commit 6003ce763c
No known key found for this signature in database
GPG key ID: C8975F2043CA5D24
7 changed files with 174 additions and 41 deletions

View file

@ -210,12 +210,14 @@ struct server_conf
#define SERVER_AUTOCONN 0x0020
#define SERVER_SSL 0x0040
#define SERVER_NO_EXPORT 0x0080
#define SERVER_SCTP 0x0100
#define ServerConfIllegal(x) ((x)->flags & SERVER_ILLEGAL)
#define ServerConfEncrypted(x) ((x)->flags & SERVER_ENCRYPTED)
#define ServerConfCompressed(x) ((x)->flags & SERVER_COMPRESSED)
#define ServerConfTb(x) ((x)->flags & SERVER_TB)
#define ServerConfAutoconn(x) ((x)->flags & SERVER_AUTOCONN)
#define ServerConfSCTP(x) ((x)->flags & SERVER_SCTP)
#define ServerConfSSL(x) ((x)->flags & SERVER_SSL)
#define ServerConfNoExport(x) ((x)->flags & SERVER_NO_EXPORT)

View file

@ -361,6 +361,7 @@ static struct mode_table connect_table[] = {
{ "compressed", SERVER_COMPRESSED },
{ "encrypted", SERVER_ENCRYPTED },
{ "topicburst", SERVER_TB },
{ "sctp", SERVER_SCTP },
{ "ssl", SERVER_SSL },
{ "no-export", SERVER_NO_EXPORT },
{ NULL, 0 },

View file

@ -1031,8 +1031,8 @@ int
serv_connect(struct server_conf *server_p, struct Client *by)
{
struct Client *client_p;
struct rb_sockaddr_storage sa_connect;
struct rb_sockaddr_storage sa_bind;
struct sockaddr_storage sa_connect[2];
struct sockaddr_storage sa_bind[ARRAY_SIZE(sa_connect)];
char note[HOSTLEN + 10];
rb_fde_t *F;
@ -1040,8 +1040,10 @@ serv_connect(struct server_conf *server_p, struct Client *by)
if(server_p == NULL)
return 0;
SET_SS_FAMILY(&sa_connect, AF_UNSPEC);
SET_SS_FAMILY(&sa_bind, AF_UNSPEC);
for (int i = 0; i < ARRAY_SIZE(sa_connect); i++) {
SET_SS_FAMILY(&sa_connect[i], AF_UNSPEC);
SET_SS_FAMILY(&sa_bind[i], AF_UNSPEC);
}
if(server_p->aftype == AF_UNSPEC
&& GET_SS_FAMILY(&server_p->connect4) == AF_INET
@ -1049,30 +1051,48 @@ serv_connect(struct server_conf *server_p, struct Client *by)
{
if(rand() % 2 == 0)
{
sa_connect = server_p->connect4;
sa_bind = server_p->bind4;
sa_connect[0] = server_p->connect4;
sa_connect[1] = server_p->connect6;
sa_bind[0] = server_p->bind4;
sa_bind[1] = server_p->bind6;
}
else
{
sa_connect = server_p->connect6;
sa_bind = server_p->bind6;
sa_connect[0] = server_p->connect6;
sa_connect[1] = server_p->connect4;
sa_bind[0] = server_p->bind6;
sa_bind[1] = server_p->bind4;
}
}
else if(server_p->aftype == AF_INET || GET_SS_FAMILY(&server_p->connect4) == AF_INET)
{
sa_connect = server_p->connect4;
sa_bind = server_p->bind4;
sa_connect[0] = server_p->connect4;
sa_bind[0] = server_p->bind4;
}
else if(server_p->aftype == AF_INET6 || GET_SS_FAMILY(&server_p->connect6) == AF_INET6)
{
sa_connect = server_p->connect6;
sa_bind = server_p->bind6;
sa_connect[0] = server_p->connect6;
sa_bind[0] = server_p->bind6;
}
/* log */
buf[0] = 0;
rb_inet_ntop_sock((struct sockaddr *)&sa_connect, buf, sizeof(buf));
ilog(L_SERVER, "Connect to *[%s] @%s", server_p->name, buf);
#ifdef HAVE_LIBSCTP
if (ServerConfSCTP(server_p) && GET_SS_FAMILY(&sa_connect[1]) != AF_UNSPEC) {
char buf2[HOSTLEN + 1];
buf[0] = 0;
buf2[0] = 0;
rb_inet_ntop_sock((struct sockaddr *)&sa_connect[0], buf, sizeof(buf));
rb_inet_ntop_sock((struct sockaddr *)&sa_connect[1], buf2, sizeof(buf2));
ilog(L_SERVER, "Connect to *[%s] @%s&%s", server_p->name, buf, buf2);
} else {
#else
{
#endif
buf[0] = 0;
rb_inet_ntop_sock((struct sockaddr *)&sa_connect[0], buf, sizeof(buf));
ilog(L_SERVER, "Connect to *[%s] @%s", server_p->name, buf);
}
/*
* Make sure this server isn't already connected
@ -1099,13 +1119,17 @@ serv_connect(struct server_conf *server_p, struct Client *by)
}
/* create a socket for the server connection */
if(GET_SS_FAMILY(&sa_connect) == AF_UNSPEC)
{
if(GET_SS_FAMILY(&sa_connect[0]) == AF_UNSPEC) {
ilog_error("unspecified socket address family");
return 0;
}
else if((F = rb_socket(GET_SS_FAMILY(&sa_connect), SOCK_STREAM, 0, NULL)) == NULL)
{
#ifdef HAVE_LIBSCTP
} else if (ServerConfSCTP(server_p)) {
if ((F = rb_socket(AF_INET6, SOCK_STREAM, IPPROTO_SCTP, NULL)) == NULL) {
ilog_error("opening a stream socket");
return 0;
}
#endif
} else if ((F = rb_socket(GET_SS_FAMILY(&sa_connect[0]), SOCK_STREAM, IPPROTO_TCP, NULL)) == NULL) {
ilog_error("opening a stream socket");
return 0;
}
@ -1126,7 +1150,8 @@ serv_connect(struct server_conf *server_p, struct Client *by)
rb_strlcpy(client_p->sockhost, buf, sizeof(client_p->sockhost));
client_p->localClient->F = F;
/* shove the port number into the sockaddr */
SET_SS_PORT(&sa_connect, htons(server_p->port));
SET_SS_PORT(&sa_connect[0], htons(server_p->port));
SET_SS_PORT(&sa_connect[1], htons(server_p->port));
/*
* Set up the initial server evilness, ripped straight from
@ -1161,19 +1186,31 @@ serv_connect(struct server_conf *server_p, struct Client *by)
SetConnecting(client_p);
rb_dlinkAddTail(client_p, &client_p->node, &global_client_list);
if(GET_SS_FAMILY(&sa_bind) == AF_UNSPEC)
{
if(GET_SS_FAMILY(&sa_connect) == GET_SS_FAMILY(&ServerInfo.bind4))
sa_bind = ServerInfo.bind4;
if(GET_SS_FAMILY(&sa_connect) == GET_SS_FAMILY(&ServerInfo.bind6))
sa_bind = ServerInfo.bind6;
for (int i = 0; i < ARRAY_SIZE(sa_connect); i++) {
if (GET_SS_FAMILY(&sa_bind[i]) == AF_UNSPEC) {
if (GET_SS_FAMILY(&sa_connect[i]) == GET_SS_FAMILY(&ServerInfo.bind4))
sa_bind[i] = ServerInfo.bind4;
if (GET_SS_FAMILY(&sa_connect[i]) == GET_SS_FAMILY(&ServerInfo.bind6))
sa_bind[i] = ServerInfo.bind6;
}
}
rb_connect_tcp(client_p->localClient->F,
(struct sockaddr *)&sa_connect,
GET_SS_FAMILY(&sa_bind) == AF_UNSPEC ? NULL : (struct sockaddr *)&sa_bind,
ServerConfSSL(server_p) ? serv_connect_ssl_callback : serv_connect_callback,
client_p, ConfigFileEntry.connect_timeout);
#ifdef HAVE_LIBSCTP
if (ServerConfSCTP(server_p)) {
rb_connect_sctp(client_p->localClient->F,
sa_connect, ARRAY_SIZE(sa_connect), sa_bind, ARRAY_SIZE(sa_bind),
ServerConfSSL(server_p) ? serv_connect_ssl_callback : serv_connect_callback,
client_p, ConfigFileEntry.connect_timeout);
} else {
#else
{
#endif
rb_connect_tcp(client_p->localClient->F,
(struct sockaddr *)&sa_connect[0],
GET_SS_FAMILY(&sa_bind[0]) == AF_UNSPEC ? NULL : (struct sockaddr *)&sa_bind[0],
ServerConfSSL(server_p) ? serv_connect_ssl_callback : serv_connect_callback,
client_p, ConfigFileEntry.connect_timeout);
}
return 1;
}

View file

@ -126,6 +126,7 @@ void rb_settimeout(rb_fde_t *, time_t, PF *, void *);
void rb_checktimeouts(void *);
void rb_connect_tcp(rb_fde_t *, struct sockaddr *, struct sockaddr *, CNCB *, void *, int);
void rb_connect_tcp_ssl(rb_fde_t *, struct sockaddr *, struct sockaddr *, CNCB *, void *, int);
void rb_connect_sctp(rb_fde_t *, struct sockaddr_storage *connect_addrs, size_t connect_len, struct sockaddr_storage *bind_addrs, size_t bind_len, CNCB *, void *, int);
int rb_connect_sockaddr(rb_fde_t *, struct sockaddr *addr, int len);
const char *rb_errstr(int status);

View file

@ -459,6 +459,25 @@ rb_bind(rb_fde_t *F, struct sockaddr *addr)
return 0;
}
#ifdef HAVE_LIBSCTP
static int
rb_sctp_bindx_only(rb_fde_t *F, struct sockaddr_storage *addrs, size_t len)
{
int ret;
for (size_t i = 0; i < len; i++) {
if (GET_SS_FAMILY(&addrs[i]) == AF_UNSPEC)
continue;
ret = sctp_bindx(F->fd, (struct sockaddr *)&addrs[i], 1, SCTP_BINDX_ADD_ADDR);
if (ret)
return ret;
}
return 0;
}
#endif
int
rb_sctp_bindx(rb_fde_t *F, struct sockaddr_storage *addrs, size_t len)
{
@ -472,14 +491,9 @@ rb_sctp_bindx(rb_fde_t *F, struct sockaddr_storage *addrs, size_t len)
if (ret)
return ret;
for (size_t i = 0; i < len; i++) {
if (GET_SS_FAMILY(&addrs[i]) == AF_UNSPEC)
continue;
ret = sctp_bindx(F->fd, (struct sockaddr *)&addrs[i], 1, SCTP_BINDX_ADD_ADDR);
if (ret)
return ret;
}
ret = rb_sctp_bindx_only(F, addrs, len);
if (ret)
return ret;
return 0;
#else
@ -647,6 +661,81 @@ rb_connect_tcp(rb_fde_t *F, struct sockaddr *dest,
rb_connect_callback(F, RB_OK);
}
void
rb_connect_sctp(rb_fde_t *F, struct sockaddr_storage *dest, size_t dest_len,
struct sockaddr_storage *clocal, size_t clocal_len,
CNCB *callback, void *data, int timeout)
{
#ifdef HAVE_LIBSCTP
uint8_t packed_dest[sizeof(struct sockaddr_storage) * dest_len];
uint8_t *p = &packed_dest[0];
size_t n = 0;
int retval;
if (F == NULL)
return;
lrb_assert(callback);
F->connect = rb_malloc(sizeof(struct conndata));
F->connect->callback = callback;
F->connect->data = data;
if ((F->type & RB_FD_SCTP) == 0) {
rb_connect_callback(F, RB_ERR_CONNECT);
return;
}
for (size_t i = 0; i < dest_len; i++) {
if (GET_SS_FAMILY(&dest[i]) == AF_INET6) {
memcpy(p, &dest[i], sizeof(struct sockaddr_in6));
n++;
p += sizeof(struct sockaddr_in6);
} else if (GET_SS_FAMILY(&dest[i]) == AF_INET) {
memcpy(p, &dest[i], sizeof(struct sockaddr_in));
n++;
p += sizeof(struct sockaddr_in);
}
}
dest_len = n;
memcpy(&F->connect->hostaddr, &dest[0], sizeof(F->connect->hostaddr));
if ((clocal_len > 0) && (rb_sctp_bindx_only(F, clocal, clocal_len) < 0)) {
/* Failure, call the callback with RB_ERR_BIND */
rb_connect_callback(F, RB_ERR_BIND);
/* ... and quit */
return;
}
rb_settimeout(F, timeout, rb_connect_timeout, NULL);
retval = sctp_connectx(F->fd, (struct sockaddr *)packed_dest, dest_len, NULL);
/* Error? */
if (retval < 0) {
/*
* If we get EISCONN, then we've already connect()ed the socket,
* which is a good thing.
* -- adrian
*/
rb_get_errno();
if (errno == EISCONN) {
rb_connect_callback(F, RB_OK);
} else if (rb_ignore_errno(errno)) {
/* Ignore error? Reschedule */
rb_setselect(F, RB_SELECT_CONNECT, rb_connect_outcome, NULL);
} else {
/* Error? Fail with RB_ERR_CONNECT */
rb_connect_callback(F, RB_ERR_CONNECT);
}
return;
}
/* If we get here, we've succeeded, so call with RB_OK */
rb_connect_callback(F, RB_OK);
#else
rb_connect_callback(F, RB_ERR_CONNECT);
#endif
}
/*
* rb_connect_callback() - call the callback, and continue with life
*/

View file

@ -18,6 +18,7 @@ rb_close
rb_connect_sockaddr
rb_connect_tcp
rb_connect_tcp_ssl
rb_connect_sctp
rb_count_rb_linebuf_memory
rb_crypt
rb_ctime

View file

@ -315,7 +315,7 @@ stats_hash(struct Client *source_p)
static void
stats_connect(struct Client *source_p)
{
static char buf[5];
static char buf[BUFSIZE];
struct server_conf *server_p;
char *s;
rb_dlink_node *ptr;
@ -342,6 +342,8 @@ stats_connect(struct Client *source_p)
{
if(ServerConfAutoconn(server_p))
*s++ = 'A';
if(ServerConfSCTP(server_p))
*s++ = 'M';
if(ServerConfSSL(server_p))
*s++ = 'S';
if(ServerConfTb(server_p))