From 0d7481570c35cd11268033cb14975943c178f1a2 Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Sat, 17 Feb 2001 13:01:15 +0000 Subject: [PATCH] updates. --- CHANGES | 11 + apps/silcd/command.c | 6 +- apps/silcd/packet_receive.c | 376 ++++++++++++++++------------ apps/silcd/server.c | 10 +- apps/silcd/server.h | 6 +- doc/draft-riikonen-silc-pp-01.nroff | 12 +- 6 files changed, 255 insertions(+), 166 deletions(-) diff --git a/CHANGES b/CHANGES index 2d24b196..66680e19 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,14 @@ +Sat Feb 17 13:15:35 EET 2001 Pekka Riikonen + + * When receiving NEW_CHANNEL_LIST, NEW_CHANNEL_USER_LIST, + NEW_ID_LIST and SET_MODE_LIST packets, broadcast the list packet + (if needs broadcasting) instead of broadcasting the packets one + by one which would make a burst in the network traffic. + + * Added `broadcast' argument to the functions in silcd/server.[ch] + silc_server_create_new_channel[_with_id] to indicate whether + to send New Channel packet to primary router. + Sat Feb 17 01:06:44 EET 2001 Pekka Riikonen * Added new function into the silcd/server.[ch] files: diff --git a/apps/silcd/command.c b/apps/silcd/command.c index 69101935..95210f7d 100644 --- a/apps/silcd/command.c +++ b/apps/silcd/command.c @@ -1983,7 +1983,7 @@ SILC_SERVER_CMD_FUNC(join) the channel by ourselves. */ if (server->standalone) { channel = silc_server_create_new_channel(server, server->id, cipher, - channel_name); + channel_name, TRUE); umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO); created = TRUE; @@ -2022,7 +2022,7 @@ SILC_SERVER_CMD_FUNC(join) if (!channel) { /* Channel really does not exist, create it */ channel = silc_server_create_new_channel(server, server->id, cipher, - channel_name); + channel_name, TRUE); umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO); created = TRUE; } @@ -2046,7 +2046,7 @@ SILC_SERVER_CMD_FUNC(join) if (!channel) { /* Channel really does not exist, create it */ channel = silc_server_create_new_channel(server, server->id, cipher, - channel_name); + channel_name, TRUE); umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO); created = TRUE; } diff --git a/apps/silcd/packet_receive.c b/apps/silcd/packet_receive.c index 9321add3..be5c1d74 100644 --- a/apps/silcd/packet_receive.c +++ b/apps/silcd/packet_receive.c @@ -648,8 +648,10 @@ SilcServerEntry silc_server_new_server(SilcServer server, /* Processes incoming New ID packet. New ID Payload is used to distribute information about newly registered clients and servers. */ -void silc_server_new_id(SilcServer server, SilcSocketConnection sock, - SilcPacketContext *packet) +static void silc_server_new_id_real(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet, + int broadcast) { SilcBuffer buffer = packet->buffer; SilcIDList id_list; @@ -683,7 +685,7 @@ void silc_server_new_id(SilcServer server, SilcSocketConnection sock, /* If the sender of this packet is server and we are router we need to broadcast this packet to other routers in the network. */ - if (!server->standalone && server->server_type == SILC_ROUTER && + if (broadcast && !server->standalone && server->server_type == SILC_ROUTER && sock->type == SILC_SOCKET_TYPE_SERVER && !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) { SILC_LOG_DEBUG(("Broadcasting received New ID packet")); @@ -780,6 +782,16 @@ void silc_server_new_id(SilcServer server, SilcSocketConnection sock, silc_id_payload_free(idp); } + +/* Processes incoming New ID packet. New ID Payload is used to distribute + information about newly registered clients and servers. */ + +void silc_server_new_id(SilcServer server, SilcSocketConnection sock, + SilcPacketContext *packet) +{ + silc_server_new_id_real(server, sock, packet, TRUE); +} + /* Receoved New Id List packet, list of New ID payloads inside one packet. Process the New ID payloads one by one. */ @@ -796,6 +808,19 @@ void silc_server_new_id_list(SilcServer server, SilcSocketConnection sock, packet->src_id_type != SILC_ID_SERVER) return; + /* If the sender of this packet is server and we are router we need to + broadcast this packet to other routers in the network. Broadcast + this list packet instead of multiple New ID packets. */ + if (!server->standalone && server->server_type == SILC_ROUTER && + sock->type == SILC_SOCKET_TYPE_SERVER && + !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) { + SILC_LOG_DEBUG(("Broadcasting received New ID List packet")); + silc_server_packet_send(server, server->router->connection, + packet->type, + packet->flags | SILC_PACKET_FLAG_BROADCAST, + packet->buffer->data, packet->buffer->len, FALSE); + } + /* Make copy of the original packet context, except for the actual data buffer, which we will here now fetch from the original buffer. */ new_id = silc_packet_context_alloc(); @@ -821,7 +846,7 @@ void silc_server_new_id_list(SilcServer server, SilcSocketConnection sock, silc_buffer_put(idp, packet->buffer->data, 4 + id_len); /* Process the New ID */ - silc_server_new_id(server, sock, new_id); + silc_server_new_id_real(server, sock, new_id, FALSE); silc_buffer_push_tail(idp, 4 + id_len); silc_buffer_pull(packet->buffer, 4 + id_len); @@ -906,7 +931,7 @@ void silc_server_new_channel(SilcServer server, if (!channel) { channel = silc_server_create_new_channel_with_id(server, NULL, channel_name, - channel_id); + channel_id, FALSE); if (!channel) return; @@ -955,6 +980,11 @@ void silc_server_new_channel(SilcServer server, silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, chk->data, chk->len, FALSE); silc_buffer_free(chk); + + /* Since the channel is coming from server and we also know about it + then send the JOIN notify to the server so that it see's our + users on the channel "joining" the channel. */ + /* XXX TODO **/ } } @@ -979,6 +1009,19 @@ void silc_server_new_channel_list(SilcServer server, server->server_type == SILC_SERVER) return; + /* If the sender of this packet is server and we are router we need to + broadcast this packet to other routers in the network. Broadcast + this list packet instead of multiple New Channel packets. */ + if (!server->standalone && server->server_type == SILC_ROUTER && + sock->type == SILC_SOCKET_TYPE_SERVER && + !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) { + SILC_LOG_DEBUG(("Broadcasting received New Channel List packet")); + silc_server_packet_send(server, server->router->connection, + packet->type, + packet->flags | SILC_PACKET_FLAG_BROADCAST, + packet->buffer->data, packet->buffer->len, FALSE); + } + /* Make copy of the original packet context, except for the actual data buffer, which we will here now fetch from the original buffer. */ new = silc_packet_context_alloc(); @@ -1019,57 +1062,71 @@ void silc_server_new_channel_list(SilcServer server, silc_free(new); } -/* Received Remove Channel User packet to remove a user from a channel. - Routers notify other routers that user has left a channel. Client must - not send this packet. Normal server may send this packet but must not - receive it. */ +/* Received new channel user packet. Information about new users on a + channel are distributed between routers using this packet. The + router receiving this will redistribute it and also sent JOIN notify + to local clients on the same channel. Normal server sends JOIN notify + to its local clients on the channel. */ -void silc_server_remove_channel_user(SilcServer server, - SilcSocketConnection sock, - SilcPacketContext *packet) +static void silc_server_new_channel_user_real(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet, + int broadcast) { - SilcBuffer buffer = packet->buffer; - unsigned char *tmp1 = NULL, *tmp2 = NULL; - unsigned short tmp1_len, tmp2_len; + unsigned char *tmpid1, *tmpid2; SilcClientID *client_id = NULL; SilcChannelID *channel_id = NULL; - SilcChannelEntry channel; + unsigned short channel_id_len; + unsigned short client_id_len; SilcClientEntry client; + SilcChannelEntry channel; + SilcChannelClientEntry chl; + SilcBuffer clidp; int ret; - SILC_LOG_DEBUG(("Removing user from channel")); + SILC_LOG_DEBUG(("Start")); if (sock->type == SILC_SOCKET_TYPE_CLIENT || - packet->src_id_type != SILC_ID_SERVER || - server->server_type == SILC_SERVER) + server->server_type != SILC_ROUTER || + packet->src_id_type != SILC_ID_SERVER) return; - ret = silc_buffer_unformat(buffer, - SILC_STR_UI16_NSTRING_ALLOC(&tmp1, &tmp1_len), - SILC_STR_UI16_NSTRING_ALLOC(&tmp2, &tmp2_len), + /* Parse payload */ + ret = silc_buffer_unformat(packet->buffer, + SILC_STR_UI16_NSTRING_ALLOC(&tmpid1, + &channel_id_len), + SILC_STR_UI16_NSTRING_ALLOC(&tmpid2, + &client_id_len), SILC_STR_END); - if (ret == -1) + if (ret == -1) { + if (tmpid1) + silc_free(tmpid1); + if (tmpid2) + silc_free(tmpid2); + return; + } + + /* Decode the channel ID */ + channel_id = silc_id_str2id(tmpid1, channel_id_len, SILC_ID_CHANNEL); + if (!channel_id) goto out; - client_id = silc_id_str2id(tmp1, tmp1_len, SILC_ID_CLIENT); - channel_id = silc_id_str2id(tmp2, tmp2_len, SILC_ID_CHANNEL); - if (!client_id || !channel_id) + /* Decode the client ID */ + client_id = silc_id_str2id(tmpid2, client_id_len, SILC_ID_CLIENT); + if (!client_id) goto out; /* If we are router and this packet is not already broadcast packet - we will broadcast it. The sending socket really cannot be router or - the router is buggy. If this packet is coming from router then it must - have the broadcast flag set already and we won't do anything. */ - if (!server->standalone && server->server_type == SILC_ROUTER && - sock->type == SILC_SOCKET_TYPE_SERVER && + we will broadcast it. */ + if (broadcast && !server->standalone && server->server_type == SILC_ROUTER && !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) { - SILC_LOG_DEBUG(("Broadcasting received Remove Channel User packet")); + SILC_LOG_DEBUG(("Broadcasting received New Channel User packet")); silc_server_packet_send(server, server->router->connection, packet->type, packet->flags | SILC_PACKET_FLAG_BROADCAST, - buffer->data, buffer->len, FALSE); + packet->buffer->data, packet->buffer->len, FALSE); } - /* Get channel entry */ + /* Find the channel */ channel = silc_idlist_find_channel_by_id(server->local_list, channel_id, NULL); if (!channel) { @@ -1088,18 +1145,48 @@ void silc_server_remove_channel_user(SilcServer server, goto out; } - /* Remove user from channel */ - silc_server_remove_from_one_channel(server, sock, channel, client, TRUE); + /* Join the client to the channel by adding it to channel's user list. + Add also the channel to client entry's channels list for fast cross- + referencing. */ + chl = silc_calloc(1, sizeof(*chl)); + chl->client = client; + chl->channel = channel; + silc_list_add(channel->user_list, chl); + silc_list_add(client->channels, chl); + + server->stat.chanclients++; + + /* Send JOIN notify to local clients on the channel. As we are router + it is assured that this is sent only to our local clients and locally + connected servers if needed. */ + clidp = silc_id_payload_encode(client_id, SILC_ID_CLIENT); + silc_server_send_notify_to_channel(server, sock, channel, FALSE, + SILC_NOTIFY_TYPE_JOIN, + 1, clidp->data, clidp->len); + silc_buffer_free(clidp); + + client_id = NULL; out: - if (tmp1) - silc_free(tmp1); - if (tmp2) - silc_free(tmp2); if (client_id) silc_free(client_id); if (channel_id) silc_free(channel_id); + silc_free(tmpid1); + silc_free(tmpid2); +} + +/* Received new channel user packet. Information about new users on a + channel are distributed between routers using this packet. The + router receiving this will redistribute it and also sent JOIN notify + to local clients on the same channel. Normal server sends JOIN notify + to its local clients on the channel. */ + +void silc_server_new_channel_user(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + silc_server_new_channel_user_real(server, sock, packet, TRUE); } /* Received New Channel User List packet, list of New Channel User payloads @@ -1120,6 +1207,17 @@ void silc_server_new_channel_user_list(SilcServer server, server->server_type == SILC_SERVER) return; + /* If we are router and this packet is not already broadcast packet + we will broadcast it. Brodcast this list packet instead of multiple + New Channel User packets. */ + if (!server->standalone && server->server_type == SILC_ROUTER && + !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) { + SILC_LOG_DEBUG(("Broadcasting received New Channel User List packet")); + silc_server_packet_send(server, server->router->connection, packet->type, + packet->flags | SILC_PACKET_FLAG_BROADCAST, + packet->buffer->data, packet->buffer->len, FALSE); + } + /* Make copy of the original packet context, except for the actual data buffer, which we will here now fetch from the original buffer. */ new = silc_packet_context_alloc(); @@ -1150,7 +1248,7 @@ void silc_server_new_channel_user_list(SilcServer server, silc_buffer_put(buffer, packet->buffer->data, 4 + len1 + len2); /* Process the New Channel User */ - silc_server_new_channel_user(server, sock, new); + silc_server_new_channel_user_real(server, sock, new, FALSE); silc_buffer_push_tail(buffer, 4 + len1 + len2); silc_buffer_pull(packet->buffer, 4 + len1 + len2); @@ -1160,6 +1258,89 @@ void silc_server_new_channel_user_list(SilcServer server, silc_free(new); } +/* Received Remove Channel User packet to remove a user from a channel. + Routers notify other routers that user has left a channel. Client must + not send this packet. Normal server may send this packet but must not + receive it. */ + +void silc_server_remove_channel_user(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + SilcBuffer buffer = packet->buffer; + unsigned char *tmp1 = NULL, *tmp2 = NULL; + unsigned short tmp1_len, tmp2_len; + SilcClientID *client_id = NULL; + SilcChannelID *channel_id = NULL; + SilcChannelEntry channel; + SilcClientEntry client; + int ret; + + SILC_LOG_DEBUG(("Removing user from channel")); + + if (sock->type == SILC_SOCKET_TYPE_CLIENT || + packet->src_id_type != SILC_ID_SERVER || + server->server_type == SILC_SERVER) + return; + + ret = silc_buffer_unformat(buffer, + SILC_STR_UI16_NSTRING_ALLOC(&tmp1, &tmp1_len), + SILC_STR_UI16_NSTRING_ALLOC(&tmp2, &tmp2_len), + SILC_STR_END); + if (ret == -1) + goto out; + + client_id = silc_id_str2id(tmp1, tmp1_len, SILC_ID_CLIENT); + channel_id = silc_id_str2id(tmp2, tmp2_len, SILC_ID_CHANNEL); + if (!client_id || !channel_id) + goto out; + + /* If we are router and this packet is not already broadcast packet + we will broadcast it. The sending socket really cannot be router or + the router is buggy. If this packet is coming from router then it must + have the broadcast flag set already and we won't do anything. */ + if (!server->standalone && server->server_type == SILC_ROUTER && + sock->type == SILC_SOCKET_TYPE_SERVER && + !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) { + SILC_LOG_DEBUG(("Broadcasting received Remove Channel User packet")); + silc_server_packet_send(server, server->router->connection, packet->type, + packet->flags | SILC_PACKET_FLAG_BROADCAST, + buffer->data, buffer->len, FALSE); + } + + /* Get channel entry */ + channel = silc_idlist_find_channel_by_id(server->local_list, + channel_id, NULL); + if (!channel) { + channel = silc_idlist_find_channel_by_id(server->global_list, + channel_id, NULL); + if (!channel) + goto out; + } + + /* Get client entry */ + client = silc_idlist_find_client_by_id(server->local_list, client_id, NULL); + if (!client) { + client = silc_idlist_find_client_by_id(server->global_list, + client_id, NULL); + if (!client) + goto out; + } + + /* Remove user from channel */ + silc_server_remove_from_one_channel(server, sock, channel, client, TRUE); + + out: + if (tmp1) + silc_free(tmp1); + if (tmp2) + silc_free(tmp2); + if (client_id) + silc_free(client_id); + if (channel_id) + silc_free(channel_id); +} + /* Received notify packet. Server can receive notify packets from router. Server then relays the notify messages to clients if needed. */ @@ -1441,119 +1622,6 @@ void silc_server_notify(SilcServer server, silc_notify_payload_free(payload); } -/* Received new channel user packet. Information about new users on a - channel are distributed between routers using this packet. The - router receiving this will redistribute it and also sent JOIN notify - to local clients on the same channel. Normal server sends JOIN notify - to its local clients on the channel. */ - -void silc_server_new_channel_user(SilcServer server, - SilcSocketConnection sock, - SilcPacketContext *packet) -{ - unsigned char *tmpid1, *tmpid2; - SilcClientID *client_id = NULL; - SilcChannelID *channel_id = NULL; - unsigned short channel_id_len; - unsigned short client_id_len; - SilcClientEntry client; - SilcChannelEntry channel; - SilcChannelClientEntry chl; - SilcBuffer clidp; - int ret; - - SILC_LOG_DEBUG(("Start")); - - if (sock->type == SILC_SOCKET_TYPE_CLIENT || - server->server_type != SILC_ROUTER || - packet->src_id_type != SILC_ID_SERVER) - return; - - /* Parse payload */ - ret = silc_buffer_unformat(packet->buffer, - SILC_STR_UI16_NSTRING_ALLOC(&tmpid1, - &channel_id_len), - SILC_STR_UI16_NSTRING_ALLOC(&tmpid2, - &client_id_len), - SILC_STR_END); - if (ret == -1) { - if (tmpid1) - silc_free(tmpid1); - if (tmpid2) - silc_free(tmpid2); - return; - } - - /* Decode the channel ID */ - channel_id = silc_id_str2id(tmpid1, channel_id_len, SILC_ID_CHANNEL); - if (!channel_id) - goto out; - - /* Decode the client ID */ - client_id = silc_id_str2id(tmpid2, client_id_len, SILC_ID_CLIENT); - if (!client_id) - goto out; - - /* Find the channel */ - channel = silc_idlist_find_channel_by_id(server->local_list, - channel_id, NULL); - if (!channel) { - channel = silc_idlist_find_channel_by_id(server->global_list, - channel_id, NULL); - if (!channel) - goto out; - } - - /* If we are router and this packet is not already broadcast packet - we will broadcast it. */ - if (!server->standalone && server->server_type == SILC_ROUTER && - !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) { - SILC_LOG_DEBUG(("Broadcasting received New Channel User packet")); - silc_server_packet_send(server, server->router->connection, packet->type, - packet->flags | SILC_PACKET_FLAG_BROADCAST, - packet->buffer->data, packet->buffer->len, FALSE); - } - - /* Get client entry */ - client = silc_idlist_find_client_by_id(server->local_list, client_id, NULL); - if (!client) { - client = silc_idlist_find_client_by_id(server->global_list, - client_id, NULL); - if (!client) - goto out; - } - - /* Join the client to the channel by adding it to channel's user list. - Add also the channel to client entry's channels list for fast cross- - referencing. */ - chl = silc_calloc(1, sizeof(*chl)); - chl->client = client; - chl->channel = channel; - silc_list_add(channel->user_list, chl); - silc_list_add(client->channels, chl); - - server->stat.chanclients++; - - /* Send JOIN notify to local clients on the channel. As we are router - it is assured that this is sent only to our local clients and locally - connected servers if needed. */ - clidp = silc_id_payload_encode(client_id, SILC_ID_CLIENT); - silc_server_send_notify_to_channel(server, sock, channel, FALSE, - SILC_NOTIFY_TYPE_JOIN, - 1, clidp->data, clidp->len); - silc_buffer_free(clidp); - - client_id = NULL; - - out: - if (client_id) - silc_free(client_id); - if (channel_id) - silc_free(channel_id); - silc_free(tmpid1); - silc_free(tmpid2); -} - /* Processes incoming REMOVE_ID packet. The packet is used to notify routers that certain ID should be removed. After that the ID will become invalid. */ diff --git a/apps/silcd/server.c b/apps/silcd/server.c index 86b0e6f5..a9e591da 100644 --- a/apps/silcd/server.c +++ b/apps/silcd/server.c @@ -2199,7 +2199,8 @@ SILC_TASK_CALLBACK(silc_server_timeout_remote) SilcChannelEntry silc_server_create_new_channel(SilcServer server, SilcServerID *router_id, char *cipher, - char *channel_name) + char *channel_name, + int broadcast) { SilcChannelID *channel_id; SilcChannelEntry entry; @@ -2230,7 +2231,7 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server, /* Notify other routers about the new channel. We send the packet to our primary route. */ - if (server->standalone == FALSE) { + if (broadcast && server->standalone == FALSE) { silc_server_send_new_channel(server, server->router->connection, TRUE, channel_name, entry->id, SILC_ID_CHANNEL_LEN); } @@ -2246,7 +2247,8 @@ SilcChannelEntry silc_server_create_new_channel_with_id(SilcServer server, char *cipher, char *channel_name, - SilcChannelID *channel_id) + SilcChannelID *channel_id, + int broadcast) { SilcChannelEntry entry; SilcCipher key; @@ -2275,7 +2277,7 @@ silc_server_create_new_channel_with_id(SilcServer server, /* Notify other routers about the new channel. We send the packet to our primary route. */ - if (server->standalone == FALSE) { + if (broadcast && server->standalone == FALSE) { silc_server_send_new_channel(server, server->router->connection, TRUE, channel_name, entry->id, SILC_ID_CHANNEL_LEN); } diff --git a/apps/silcd/server.h b/apps/silcd/server.h index f8b7c6f7..13b0dff2 100644 --- a/apps/silcd/server.h +++ b/apps/silcd/server.h @@ -110,12 +110,14 @@ void silc_server_disconnect_remote(SilcServer server, SilcChannelEntry silc_server_create_new_channel(SilcServer server, SilcServerID *router_id, char *cipher, - char *channel_name); + char *channel_name, + int broadcast); SilcChannelEntry silc_server_create_new_channel_with_id(SilcServer server, char *cipher, char *channel_name, - SilcChannelID *channel_id); + SilcChannelID *channel_id, + int broadcast); void silc_server_create_channel_key(SilcServer server, SilcChannelEntry channel, unsigned int key_len); diff --git a/doc/draft-riikonen-silc-pp-01.nroff b/doc/draft-riikonen-silc-pp-01.nroff index 708dfe16..a8867c45 100644 --- a/doc/draft-riikonen-silc-pp-01.nroff +++ b/doc/draft-riikonen-silc-pp-01.nroff @@ -1775,7 +1775,8 @@ This payload is used, for example, when server is connected to router and the server wants to distribute all of its locally connected clients and locally created channels to the router. It is convenient in this case to use this payload instead of sending all the information one -by one using New ID Payload. +by one using New ID Payload. Also, when router redistributes the +packet by broadcasting it, it is convenient to send only one packet. There is no specific payload for this packet type. The packet type uses same payload as described in previous section. To form a list @@ -2020,7 +2021,9 @@ o Client ID (variable length) - The Client ID of the client This payload is used to distribute list of new channels from server to routers. It might be convenient to send list of new channels when existing server connects to router, instead of sending them one -by one. +by one. Also, when router redistributes the packet by broadcasting it, +it is convenient to send only one packet. + There is no specific payload for this packet type. The packet type uses same payload as described in 2.3.19 New Channel Payload. To form @@ -2040,6 +2043,8 @@ This payload is used to distribute list of channel users on a channel from server to routers. It might convenient to send list of channel users when existing server connects to router, instead of sending them one by one. One list may include users for several different channels. +Also, when router redistributes the packet by broadcasting it, it is +convenient to send only one packet. There is no specific payload for this packet type. The packet type uses same payload as described in 2.3.20 New Channel User Payload. @@ -2269,7 +2274,8 @@ one packet. When server announces channels and client's on those channels to its primary router when it connects to it, it is convenient to send list of Set Mode payloads to set the modes for the channel and clients on those channel. One list may include several mode -types. +types. Also, when router redistributes the packet by broadcasting it, +it is convenient to send only one packet. There is no specific payload for this packet type. The packet type uses same payload as described in 2.3.27 Set Mode Payload. To form a -- 2.24.0