revised bootstrap (#599)

This commit is contained in:
Logan oos Even 2021-01-25 16:54:38 +05:45 committed by GitHub
parent 5cd1c9030c
commit c0c472b4aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 168 additions and 52 deletions

View File

@ -38,6 +38,7 @@
/* Space needed to store socket and MAC address of a supernode */
#define REG_SUPER_ACK_PAYLOAD_ENTRY_SIZE (sizeof(n2n_REGISTER_SUPER_ACK_payload_t))
#define BOOTSTRAP_TIMEOUT 3
#define PURGE_REGISTRATION_FREQUENCY 30
#define RE_REG_AND_PURGE_FREQUENCY 10
#define REGISTRATION_TIMEOUT 60

View File

@ -617,6 +617,7 @@ struct n2n_edge {
/* Status */
struct peer_info *curr_sn; /**< Currently active supernode. */
uint8_t sn_wait; /**< Whether we are waiting for a supernode response. */
uint8_t sn_pong; /**< Whether we have seen a PONG since last time reset. */
size_t sup_attempts; /**< Number of remaining attempts to this supernode. */
tuntap_dev device; /**< All about the TUNTAP device */
n2n_trans_op_t transop; /**< The transop to use when encoding */

View File

@ -42,7 +42,10 @@ static cap_value_t cap_values[] = {
int num_cap = sizeof(cap_values)/sizeof(cap_value_t);
#endif
// forward declaration for use in main()
void send_register_super (n2n_edge_t *eee);
void send_query_peer (n2n_edge_t * eee, const n2n_mac_t dst_mac);
/* ***************************************************** */
@ -704,10 +707,17 @@ BOOL WINAPI term_handler(DWORD sig)
int main (int argc, char* argv[]) {
int rc;
tuntap_dev tuntap; /* a tuntap device */
n2n_edge_t *eee; /* single instance for this program */
n2n_edge_conf_t conf; /* generic N2N edge config */
n2n_tuntap_priv_config_t ec; /* config used for standalone program execution */
tuntap_dev tuntap; /* a tuntap device */
n2n_edge_t *eee; /* single instance for this program */
n2n_edge_conf_t conf; /* generic N2N edge config */
n2n_tuntap_priv_config_t ec; /* config used for standalone program execution */
uint8_t runlevel = 0; /* bootstrap: runlevel */
uint8_t seek_answer = 1; /* expecting answer from supernode */
time_t now_time, last_action; /* timeout */
macstr_t mac_buf; /* output mac address */
fd_set socket_mask; /* for supernode answer */
struct timeval wait_time; /* timeout for sn answer */
#ifndef WIN32
struct passwd *pw = NULL;
#endif
@ -806,24 +816,120 @@ int main (int argc, char* argv[]) {
} else {
traceEvent(TRACE_NORMAL, "Automatically assign IP address by supernode.");
eee->conf.tuntap_ip_mode = TUNTAP_IP_MODE_SN_ASSIGN;
}
// REVISIT: integrate into the (to be created) bootstrap, maybe even as part of a more stateful main loop
eee->sn_wait = 1;
do {
fd_set socket_mask;
struct timeval wait_time;
// mini main loop for bootstrap, not using main loop code because some of its mechanisms do not fit in here
// for the sake of quickly establishing connection. REVISIT when a more elegant way to re-use main loop code
// is found
// next supernode
if (eee->curr_sn->hh.next)
eee->curr_sn = eee->curr_sn->hh.next;
else
// if more than one supernode given, find at least one who is alive to faster establish connection
if(HASH_COUNT(eee->conf.supernodes) <= 1) {
// skip the initial supernode ping
traceEvent(TRACE_DEBUG, "Skip PING to supernode.");
runlevel = 2;
}
eee->last_sup = 1; /* to prevent gratuitous arp packet */
eee->curr_sn = eee->conf.supernodes;
while(runlevel < 5) {
now_time = time(NULL);
// we do not use switch-case because we also check for 'greater than'
if(runlevel == 0) { /* PING to all known supernodes */
last_action = now_time;
eee->sn_pong = 0;
send_query_peer(eee, null_mac);
traceEvent(TRACE_NORMAL, "Send PING to supernodes.");
runlevel++;
}
if(runlevel == 1) { /* PING has been sent to all known supernodes */
if(eee->sn_pong) {
// first answer
eee->sn_pong = 0;
sn_selection_sort(&(eee->conf.supernodes));
eee->curr_sn = eee->conf.supernodes;
traceEvent(TRACE_NORMAL, "Received first PONG from supernode [%s].", eee->curr_sn->ip_addr);
runlevel++;
}
if(last_action <= (now_time - BOOTSTRAP_TIMEOUT)) {
// timeout
runlevel--;
// skip waiting for answer to direcly go to send PING again
seek_answer = 0;
traceEvent(TRACE_DEBUG, "PONG timeout.");
}
}
send_register_super(eee);
// by the way, have every later PONG cause the remaining (!) list to be sorted because the entries
// before have already been tried; as opposed to initial PONG, do not change curr_sn
if(runlevel > 1) {
if(eee->sn_pong) {
eee->sn_pong = 0;
if(eee->curr_sn->hh.next) {
sn_selection_sort((peer_info_t**)&(eee->curr_sn->hh.next));
traceEvent(TRACE_DEBUG, "Received additional PONG from supernode.");
// here, it is hard to detemine from which one, so no details to output
}
}
}
if(runlevel == 2) { /* send REGISTER_SUPER to get auto ip address from a supernode */
if(eee->conf.tuntap_ip_mode == TUNTAP_IP_MODE_SN_ASSIGN) {
last_action = now_time;
eee->sn_wait = 1;
send_register_super(eee);
runlevel++;
traceEvent(TRACE_NORMAL, "Send REGISTER_SUPER to supernode [%s] asking for IP address.",
eee->curr_sn->ip_addr);
} else {
runlevel += 2; /* skip waiting for TUNTAP IP address */
traceEvent(TRACE_DEBUG, "Skip auto IP address asignment.");
}
}
if(runlevel == 3) { /* REGISTER_SUPER to get auto ip address from a sn has been sent */
if(!eee->sn_wait) { /* TUNTAP IP address received */
runlevel++;
traceEvent(TRACE_NORMAL, "Received REGISTER_SUPER_ACK from supernode for IP address asignment.");
// it should be from curr_sn, but we can't determine definitely here, so no details to output
}
if(last_action <= (now_time - BOOTSTRAP_TIMEOUT)) {
// timeout, so try next supernode
if(eee->curr_sn->hh.next)
eee->curr_sn = eee->curr_sn->hh.next;
else
eee->curr_sn = eee->conf.supernodes;
runlevel--;
// skip waiting for answer to direcly go to send REGISTER_SUPER again
seek_answer = 0;
traceEvent(TRACE_DEBUG, "REGISTER_SUPER_ACK timeout.");
}
}
if(runlevel == 4) { /* configure the TUNTAP device */
if(tuntap_open(&tuntap, eee->tuntap_priv_conf.tuntap_dev_name, eee->tuntap_priv_conf.ip_mode,
eee->tuntap_priv_conf.ip_addr, eee->tuntap_priv_conf.netmask,
eee->tuntap_priv_conf.device_mac, eee->tuntap_priv_conf.mtu) < 0)
exit(1);
memcpy(&eee->device, &tuntap, sizeof(tuntap));
traceEvent(TRACE_NORMAL, "Created local tap device IP: %s, Mask: %s, MAC: %s",
eee->tuntap_priv_conf.ip_addr,
eee->tuntap_priv_conf.netmask,
macaddr_str(mac_buf, eee->device.mac_addr));
runlevel = 5;
// no more answers required
seek_answer = 0;
}
// we usually wait for some answer, there however are exceptions when going back to a previous runlevel
if(seek_answer) {
FD_ZERO(&socket_mask);
FD_SET(eee->udp_sock, &socket_mask);
wait_time.tv_sec = (SOCKET_TIMEOUT_INTERVAL_SECS / 10) + 1;
wait_time.tv_sec = BOOTSTRAP_TIMEOUT;
wait_time.tv_usec = 0;
if(select(eee->udp_sock + 1, &socket_mask, NULL, NULL, &wait_time) > 0) {
@ -831,17 +937,12 @@ int main (int argc, char* argv[]) {
readFromIPSocket(eee, eee->udp_sock);
}
}
} while(eee->sn_wait);
eee->last_register_req = 0;
}
seek_answer = 1;
}
if(tuntap_open(&tuntap, eee->tuntap_priv_conf.tuntap_dev_name, eee->tuntap_priv_conf.ip_mode,
eee->tuntap_priv_conf.ip_addr, eee->tuntap_priv_conf.netmask,
eee->tuntap_priv_conf.device_mac, eee->tuntap_priv_conf.mtu) < 0) exit(1);
traceEvent(TRACE_NORMAL, "Local tap IP: %s, Mask: %s",
eee->tuntap_priv_conf.ip_addr, eee->tuntap_priv_conf.netmask);
memcpy(&eee->device, &tuntap, sizeof(tuntap));
//hexdump((unsigned char*)&tuntap,sizeof(tuntap_dev));
eee->sn_wait = 1;
eee->last_register_req = 0;
eee->last_sup = 0; /* to allow gratuitous arp packet after regular REGISTER_SUPER_ACK */
#ifndef WIN32
if(eee->tuntap_priv_conf.daemon) {

View File

@ -761,7 +761,7 @@ static void check_join_multicast_group (n2n_edge_t *eee) {
/* ************************************** */
/** Send a QUERY_PEER packet to the current supernode. */
static void send_query_peer (n2n_edge_t * eee,
void send_query_peer (n2n_edge_t * eee,
const n2n_mac_t dst_mac) {
uint8_t pktbuf[N2N_PKT_BUF_SIZE];
@ -903,6 +903,7 @@ static int sort_supernodes (n2n_edge_t *eee, time_t now) {
struct peer_info *scan, *tmp;
if(eee->curr_sn != eee->conf.supernodes) {
// have not been connected to the best/top one
send_unregister_super(eee);
eee->curr_sn = eee->conf.supernodes;
@ -920,7 +921,6 @@ static int sort_supernodes (n2n_edge_t *eee, time_t now) {
// this routine gets periodically called
// it sorts supernodes in ascending order of their selection_criterion fields
sn_selection_sort(&(eee->conf.supernodes));
}
HASH_ITER(hh, eee->conf.supernodes, scan, tmp) {
@ -928,8 +928,12 @@ static int sort_supernodes (n2n_edge_t *eee, time_t now) {
}
sn_selection_criterion_common_data_default(eee);
// send PING to all the supernodes
send_query_peer(eee, null_mac);
eee->last_sweep = now;
// no answer yet (so far, unused in regular edge code; mainly used during bootstrap loading)
eee->sn_pong = 0;
}
return 0; /* OK */
@ -2104,13 +2108,6 @@ void readFromIPSocket (n2n_edge_t * eee, int in_sock) {
memset(&ra, 0, sizeof(n2n_REGISTER_SUPER_ACK_t));
// Indicates successful connection between the edge and SN nodes
static int bTrace = 1;
if(bTrace) {
traceEvent(TRACE_NORMAL, "[OK] Edge Peer <<< ================ >>> Super Node");
bTrace = 0;
}
if(eee->sn_wait) {
decode_REGISTER_SUPER_ACK(&ra, &cmn, udp_buf, &rem, &idx, tmpbuf);
@ -2122,7 +2119,7 @@ void readFromIPSocket (n2n_edge_t * eee, int in_sock) {
}
if(is_valid_peer_sock(&ra.sock))
orig_sender = &(ra.sock);
orig_sender = &(ra.sock);
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK myMAC=%s [%s] (external %s). Attempts %u",
macaddr_str(mac_buf1, ra.edgeMac),
@ -2130,6 +2127,7 @@ void readFromIPSocket (n2n_edge_t * eee, int in_sock) {
sock_to_cstr(sockbuf2, orig_sender),
(unsigned int)eee->sup_attempts);
// this even holds true for auto ip assignment as own mac is null_mac
if(memcmp(ra.edgeMac, eee->device.mac_addr, N2N_MAC_SIZE)) {
traceEvent(TRACE_INFO, "readFromIPSocket dropped REGISTER_SUPER_ACK due to wrong addressing.");
return;
@ -2173,20 +2171,24 @@ void readFromIPSocket (n2n_edge_t * eee, int in_sock) {
}
}
if(!eee->last_sup) // send gratuitous ARP only upon first registration with supernode
if(!eee->last_sup) {
// indicates successful connection between the edge and a supernode
traceEvent(TRACE_NORMAL, "[OK] Edge Peer <<< ================ >>> Super Node");
// send gratuitous ARP only upon first registration with supernode
send_grat_arps(eee);
}
eee->last_sup = now;
eee->sn_wait = 0;
eee->sup_attempts = N2N_EDGE_SUP_ATTEMPTS; /* refresh because we got a response */
if(eee->cb.sn_registration_updated)
eee->cb.sn_registration_updated(eee, now, &sender);
/* NOTE: the register_interval should be chosen by the edge node
* based on its NAT configuration. */
//eee->conf.register_interval = ra.lifetime;
if(eee->cb.sn_registration_updated && !is_null_mac(ra.edgeMac))
eee->cb.sn_registration_updated(eee, now, &sender);
} else {
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK with wrong or old cookie.");
}
@ -2247,15 +2249,18 @@ void readFromIPSocket (n2n_edge_t * eee, int in_sock) {
}
if(is_null_mac(pi.mac)) {
// PONG - answer to PING (QUERY_PEER_INFO with null mac)
skip_add = SN_ADD_SKIP;
scan = add_sn_to_list_by_mac_or_sock(&(eee->conf.supernodes), &sender, pi.srcMac, &skip_add);
if(scan != NULL) {
eee->sn_pong = 1;
scan->last_seen = now;
/* The data type depends on the actual selection strategy that has been chosen. */
sn_selection_criterion_calculate(eee, scan, &pi.data);
break;
}
} else {
// regular PEER_INFO
HASH_FIND_PEER(eee->pending_peers, pi.mac, scan);
if(scan) {
@ -2423,11 +2428,11 @@ int run_edge_loop (n2n_edge_t * eee, int *keep_running) {
eee->cb.ip_address_changed(eee, old_ip, eee->device.ip_addr);
}
sort_supernodes(eee, nowTime);
if(eee->cb.main_loop_period)
eee->cb.main_loop_period(eee, nowTime);
sort_supernodes(eee, nowTime);
} /* while */
#ifdef WIN32

View File

@ -1365,22 +1365,30 @@ static int process_udp (n2n_sn_t * sss,
traceEvent(TRACE_DEBUG, "Tx REGISTER_SUPER_NAK for %s",
macaddr_str(mac_buf, reg.edgeMac));
} else {
// if this is not already forwarded from a supernode, ...
if(!(cmn.flags & N2N_FLAGS_SOCKET)) {
reg.sock.family = AF_INET;
reg.sock.port = ntohs(sender_sock->sin_port);
memcpy(reg.sock.addr.v4, &(sender_sock->sin_addr.s_addr), IPV4_SIZE);
// ... forward to all other supernodes (note try_broadcast()'s behavior with
// NULL comm and from_supernode parameter)
cmn2.pc = n2n_register_super;
encode_REGISTER_SUPER(ackbuf, &encx, &cmn2, &reg);
// exception: do not forward auto ip draw
if(!is_null_mac(reg.edgeMac)) {
reg.sock.family = AF_INET;
reg.sock.port = ntohs(sender_sock->sin_port);
memcpy(reg.sock.addr.v4, &(sender_sock->sin_addr.s_addr), IPV4_SIZE);
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) {
packet_header_encrypt(ackbuf, encx, encx,
comm->header_encryption_ctx, comm->header_iv_ctx,
time_stamp());
cmn2.pc = n2n_register_super;
encode_REGISTER_SUPER(ackbuf, &encx, &cmn2, &reg);
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) {
packet_header_encrypt(ackbuf, encx, encx,
comm->header_encryption_ctx, comm->header_iv_ctx,
time_stamp());
}
try_broadcast(sss, NULL, &cmn, reg.edgeMac, from_supernode, ackbuf, encx);
}
try_broadcast(sss, NULL, &cmn, reg.edgeMac, from_supernode, ackbuf, encx);
// send REGISTER_SUPER_ACK
encx = 0;
cmn2.pc = n2n_register_super_ack;