diff --git a/src/conf.h b/src/conf.h index 1dae5fd3..f152bb35 100644 --- a/src/conf.h +++ b/src/conf.h @@ -25,6 +25,9 @@ #include "splay_tree.h" #include "subnet.h" +#define DEFAULT_SLPD_GROUP "224.0.42.23" +#define DEFAULT_SLPD_PORT "1655" + typedef struct config_t { char *variable; char *value; diff --git a/src/net.h b/src/net.h index d26273cb..1e77d3d2 100644 --- a/src/net.h +++ b/src/net.h @@ -153,6 +153,7 @@ extern int listen_sockets; extern io_t unix_socket; extern int keylifetime; extern int edgeupdateinterval; +extern int slpdinterval; extern int udp_rcvbuf; extern int udp_sndbuf; extern int max_connection_burst; @@ -188,6 +189,7 @@ extern char *scriptextension; extern void retry_outgoing(outgoing_t *); extern void handle_incoming_vpn_data(void *, int); +extern void handle_incoming_slpd_data(void *, int); extern void finish_connecting(struct connection_t *); extern bool do_outgoing_connection(struct outgoing_t *); extern void handle_new_meta_connection(void *, int); @@ -215,8 +217,10 @@ extern bool read_ecdsa_public_key(struct connection_t *); extern bool read_rsa_public_key(struct connection_t *); extern void handle_device_data(void *, int); extern void handle_meta_connection_data(struct connection_t *); +extern void send_slpd_broadcast(void); extern void regenerate_key(void); extern void update_edge_weight(void); +extern int setup_slpd_in_socket(void); extern void purge(void); extern void retry(void); extern int reload_configuration(void); diff --git a/src/net_packet.c b/src/net_packet.c index 92a142f3..0a50aae5 100644 --- a/src/net_packet.c +++ b/src/net_packet.c @@ -59,6 +59,7 @@ int keylifetime = 0; int edgeupdateinterval = 0; +int slpdinterval = 0; #ifdef HAVE_LZO static char lzo_wrkmem[LZO1X_999_MEM_COMPRESS > LZO1X_1_MEM_COMPRESS ? LZO1X_999_MEM_COMPRESS : LZO1X_1_MEM_COMPRESS]; #endif @@ -1514,6 +1515,51 @@ skip_harder: send_mtu_info(myself, n, MTU); } +static void handle_incoming_slpd_packet(listen_socket_t *ls, void *pkt, sockaddr_t *addr) { + + int mav, miv, port; + char nodename[MAXSIZE], fng[MAXSIZE]; + + int i = sscanf(pkt, "sLPD %d %d %s %d %s", &mav, &miv, &nodename[0], &port, &fng[0]); + if (i != 5) { + logger(DEBUG_ALWAYS, LOG_ERR, "cant not parse packet... %d",i); + return; + } + + if (mav == 0 && miv == 1) { + + logger(DEBUG_TRAFFIC, LOG_ERR, "Got SLPD packet node:%s port:%d %d.%d <%s> from %s", nodename, port, mav, miv, fng, inet_ntoa(addr->in.sin_addr)); + + node_t *n = lookup_node(nodename); + if (!n) { + logger(DEBUG_ALWAYS, LOG_ERR, "unknown node: %s", nodename); + return; + } + + if (!strncmp(n->name, myself->name, strlen(myself->name))) { + logger(DEBUG_SCARY_THINGS, LOG_NOTICE, "Ignore SLPD for myself: %s", nodename); + return; + } + + config_t *cfg = NULL; + if (!n->slpd_address) { + cfg = new_config(); + cfg->variable = xstrdup("Address"); + cfg->value = xstrdup(inet_ntoa(addr->in.sin_addr)); + cfg->file = NULL; + cfg->line = -1; + + logger(DEBUG_ALWAYS, LOG_ERR, "Discovered %s on %s", nodename, inet_ntoa(addr->in.sin_addr)); + + n->slpd_address = cfg; + n->status.has_address = true; + } + } else { + logger(DEBUG_ALWAYS, LOG_ERR, "Got SLPD packet with wrong version %d.%d", mav, miv); + } + return; +} + void handle_incoming_vpn_data(void *data, int flags) { listen_socket_t *ls = data; @@ -1575,6 +1621,27 @@ void handle_incoming_vpn_data(void *data, int flags) { #endif } +void handle_incoming_slpd_data(void *data, int flags) { + listen_socket_t *ls = data; + + void *pkt; + sockaddr_t addr = {}; + socklen_t addrlen = sizeof addr; + + logger(DEBUG_SCARY_THINGS, LOG_INFO, "Receiving LPD packet"); + + int len = recvfrom(ls->udp.fd, (void *)&pkt, MAXSIZE, 0, &addr.sa, &addrlen); + + if(len <= 0 || len > MAXSIZE) { + if(!sockwouldblock(sockerrno)) + logger(DEBUG_ALWAYS, LOG_ERR, "Receiving SLPD packet failed: %s", sockstrerror(sockerrno)); + return; + } + + handle_incoming_slpd_packet(ls, &pkt, &addr); +} + + void handle_device_data(void *data, int flags) { vpn_packet_t packet; UNUSED(data); diff --git a/src/net_setup.c b/src/net_setup.c index 85383a69..485d5292 100644 --- a/src/net_setup.c +++ b/src/net_setup.c @@ -49,6 +49,8 @@ #endif char *myport; +char *my_slpd_port; +char *my_slpd_group; static char *myname; static io_t device_io; devops_t devops; @@ -313,6 +315,13 @@ static bool read_rsa_private_key(void) { static timeout_t keyexpire_timeout; static timeout_t edgeupdate_timeout; +/* Local Peer Discovery */ +static timeout_t slpdupdate_timeout; + +static void slpdupdate_handler(void *data) { + send_slpd_broadcast(); + timeout_set(data, &(struct timeval){slpdinterval + (rand() % 10), rand() % 100000}); +} static void keyexpire_handler(void *data) { regenerate_key(); @@ -324,6 +333,56 @@ static void edgeupdate_handler(void *data) { timeout_set(data, &(struct timeval){edgeupdateinterval + (rand() % 10), rand() % 100000}); } +void send_slpd_broadcast(void) { + int sd; + struct sockaddr_in address; + char slpd_msg[MAXSIZE] = ""; + char *iface; + struct in_addr lif; + struct ifreq ifr; + int fd; + + sd = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sd < 0) { + logger(DEBUG_ALWAYS, LOG_INFO, "socket() error: [%s:%d]", strerror(errno), errno); + return; + } + + /* Send SLPD only on this Interface */ + if(get_config_string (lookup_config (config_tree, "SLPDInterface"), &iface)) { + fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + ifr.ifr_addr.sa_family = AF_INET; + strncpy(ifr.ifr_name, iface, IFNAMSIZ-1); + ioctl(fd, SIOCGIFADDR, &ifr); + close(fd); + + /* display result */ + logger(DEBUG_ALWAYS, LOG_ERR, "%s has the ip: %s\n", iface, + inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr)); + + lif.s_addr = inet_addr(inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr)); + if(setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, (char *)&lif, sizeof(lif)) < 0) { + logger(DEBUG_ALWAYS, LOG_ERR, "can not bind multicast to %s\n", iface); + return; + } + } + + memset (&address, 0, sizeof (address)); + address.sin_family = AF_INET; + address.sin_addr.s_addr = inet_addr (DEFAULT_SLPD_GROUP); + address.sin_port = htons(atoi(my_slpd_port)); + + snprintf(slpd_msg, MAXSIZE, "sLPD 0 1 %s %d none ", myname, atoi(myport)); + slpd_msg[MAXSIZE-1] = '\00'; + //ecdsa_sign(myself->sptps.mykey, msg, strlen(msg), sig); + + if (sendto( sd, slpd_msg, strlen(slpd_msg), 0, (struct sockaddr *) &address, sizeof (address)) < 0) { + logger(DEBUG_ALWAYS, LOG_ERR, "SLPD send() error: [%s:%d]", strerror(errno), errno); + } + close(sd); + return; +} + void regenerate_key(void) { logger(DEBUG_STATUS, LOG_INFO, "Expiring symmetric keys"); send_key_changed(); @@ -642,6 +701,9 @@ bool setup_myself_reloadable(void) { if(!get_config_int(lookup_config(config_tree, "EdgeUpdateInterval"), &edgeupdateinterval)) edgeupdateinterval = 0; + if(!get_config_int(lookup_config(config_tree, "SLPDInterval"), &slpdinterval)) + slpdinterval = 0; + config_t *cfg = lookup_config(config_tree, "AutoConnect"); if(cfg) { if(!get_config_bool(cfg, &autoconnect)) { @@ -803,6 +865,12 @@ static bool setup_myself(void) { else port_specified = true; + if(!get_config_string(lookup_config(config_tree, "SLPDPort"), &my_slpd_port)) + my_slpd_port = xstrdup(DEFAULT_SLPD_PORT); + + if(!get_config_string(lookup_config(config_tree, "SLPDGroup"), &my_slpd_group)) + my_slpd_group = xstrdup(DEFAULT_SLPD_GROUP); + myself->connection->options = 0; myself->connection->protocol_major = PROT_MAJOR; myself->connection->protocol_minor = PROT_MINOR; @@ -942,6 +1010,8 @@ static bool setup_myself(void) { timeout_add(&edgeupdate_timeout, edgeupdate_handler, &edgeupdate_timeout, &(struct timeval){edgeupdateinterval, rand() % 100000}); if (slpdinterval) + timeout_add(&slpdupdate_timeout, slpdupdate_handler, &slpdupdate_timeout, &(struct timeval){slpdinterval, rand() % 100000}); + /* Compression */ if(get_config_int(lookup_config(config_tree, "Compression"), &myself->incompression)) { @@ -1062,6 +1132,16 @@ static bool setup_myself(void) { return false; } + if (slpdinterval) { + int slpd_fd = setup_slpd_in_socket(); + if (slpd_fd < 0) { + close(slpd_fd); + } else { + logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Listening on multicast group"); + io_add(&listen_socket[listen_sockets].udp, handle_incoming_slpd_data, &listen_socket[listen_sockets], slpd_fd, IO_READ); + } + } + if(!listen_sockets) { logger(DEBUG_ALWAYS, LOG_ERR, "Unable to create any listening socket!"); return false; diff --git a/src/net_socket.c b/src/net_socket.c index 25cde5c0..c629ee29 100644 --- a/src/net_socket.c +++ b/src/net_socket.c @@ -301,6 +301,58 @@ int setup_vpn_in_socket(const sockaddr_t *sa) { return nfd; } /* int setup_vpn_in_socket */ + +int setup_slpd_in_socket(void) { + int nfd; + char *my_slpd_port; + char *my_slpd_group; + struct sockaddr_in lsock; + struct ip_mreq group; + struct ip_mreq mreq; + + if(!get_config_string(lookup_config(config_tree, "SLPDPort"), &my_slpd_port)) + my_slpd_port = xstrdup(DEFAULT_SLPD_PORT); + + if(!get_config_string(lookup_config(config_tree, "SLPDGroup"), &my_slpd_group)) + my_slpd_group = xstrdup(DEFAULT_SLPD_GROUP); + + nfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + if(nfd < 0) { + logger(DEBUG_ALWAYS, LOG_ERR, "Can not create socket for SLPD %s", sockstrerror(sockerrno)); + return -1; + } + + int reuse = 1; + if(setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0) { + logger(DEBUG_ALWAYS, LOG_ERR, "Can not set SO_REUSEADDR for SLPD %s", sockstrerror(sockerrno)); + closesocket(nfd); + return -1; + } + + memset((char *) &lsock, 0, sizeof(lsock)); + lsock.sin_family = AF_INET; + lsock.sin_port = htons(atoi(my_slpd_port)); + lsock.sin_addr.s_addr = htonl(INADDR_ANY); + + if(bind(nfd, (struct sockaddr*)&lsock, sizeof(lsock)) < 0) { + logger(DEBUG_ALWAYS, LOG_ERR, "Can not bind() socket for SLPD %s", sockstrerror(sockerrno)); + closesocket(nfd); + return -1; + } + + group.imr_multiaddr.s_addr = inet_addr(my_slpd_group); + group.imr_interface.s_addr = htonl(INADDR_ANY); + if(setsockopt(nfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&group, sizeof(group)) < 0) { + logger(DEBUG_ALWAYS, LOG_ERR, "Can not join group for SLPD %s", sockstrerror(sockerrno)); + closesocket(nfd); + return -1; + } + logger(DEBUG_STATUS, LOG_INFO, "SLPD socket ready (%d)", nfd); + + return nfd; +} /* int setup_slpd_in_socket */ + static void retry_outgoing_handler(void *data) { setup_outgoing_connection(data); } @@ -619,6 +671,9 @@ void setup_outgoing_connection(outgoing_t *outgoing) { outgoing->cfg = lookup_config(outgoing->config_tree, "Address"); } + if (slpdinterval && !outgoing->cfg && n->slpd_address) + outgoing->cfg = n->slpd_address; + if(!outgoing->cfg) { if(n) outgoing->aip = outgoing->ai = get_known_addresses(n); diff --git a/src/node.h b/src/node.h index 65ec21f6..1eec712a 100644 --- a/src/node.h +++ b/src/node.h @@ -23,6 +23,7 @@ #include "splay_tree.h" #include "cipher.h" +#include "conf.h" #include "connection.h" #include "digest.h" #include "event.h" @@ -81,7 +82,7 @@ typedef struct node_t { splay_tree_t *edge_tree; /* Edges with this node as one of the endpoints */ struct connection_t *connection; /* Connection associated with this node (if a direct connection exists) */ - + struct config_t *slpd_address; /* Address we learned via SLPD */ uint32_t sent_seqno; /* Sequence number last sent to this node */ uint32_t received_seqno; /* Sequence number last received from this node */ uint32_t received; /* Total valid packets received from this node */