/* Define to 1 if you have the stpcpy function. */
#undef HAVE_STPCPY
-
/* For getopt */
#if HAVE_STDLIB_H
# define getopt system_getopt
# undef getopt
#endif
+/* Linux */
+#undef HAVE_LINUX
+
+/* FreeBSD */
+#undef HAVE_FREEBSD
+
+/* OpenBSD */
+#undef HAVE_OPENBSD
+
+/* Solaris */
+#undef HAVE_SOLARIS
+
+/* NetBSD */
+#undef HAVE_NETBSD
/* Define to the location of the kernel sources */
#undef CONFIG_TINC_KERNELDIR
/* Define to the location of if_tun.h */
#undef LINUX_IF_TUN_H
+
+/* Define to 1 if support for jumbograms is enabled */
+#undef ENABLE_JUMBOGRAMS
+
+/* Define to 1 if checkpoint tracing is enabled */
+#undef ENABLE_TRACING
+
+/* Define to enable use of old SSLeay_add_all_algorithms() function */
+#undef HAVE_SSLEAY_ADD_ALL_ALGORITHMS
--- /dev/null
+## Process this file with automake to get Makefile.in
+
+# Nothing to see here, go away!
--- /dev/null
+# Sample host configuration file
+
+# The real IP address of this tinc host. Can be used by other tinc hosts.
+Address = 123.234.35.67
+
+# Portnumber for incoming connections. Default is 655.
+Port = 655
+
+# Subnet on the virtual private network that is local for this host.
+Subnet = 192.168.1.0/24
+
+# The public key generated by `tincd -n example -K' is stored here
+-----BEGIN RSA PUBLIC KEY-----
+...
+-----END RSA PUBLIC KEY-----
--- /dev/null
+# Sample host configuration file
+# This file was generated by host beta.
+
+# The real IP address of this tinc host. Can be used by other tinc hosts.
+Address = 123.45.67.189
+
+# Portnumber for incoming connections. Default is 655.
+Port = 6500
+
+# Subnet on the virtual private network that is local for this host.
+Subnet = 192.168.2.0/24
+
+# The public key generated by `tincd -n example -K' is stored here
+-----BEGIN RSA PUBLIC KEY-----
+...
+-----END RSA PUBLIC KEY-----
--- /dev/null
+# Generate this file with `tincd -n example -K`
--- /dev/null
+#!/bin/sh
+# This file closes down the tap device.
+
+ifconfig $INTERFACE down
--- /dev/null
+#!/bin/sh
+# This file sets up the tap device.
+# It gives you the freedom to do anything you want with it.
+# Use the correct name for the tap device:
+# For the Linux tun/tap device $INTERFACE is set to the right name,
+# but for ethertap and FreeBSD this is tap0, tap1, tap2 etcetera,
+# for Solaris and OpenBSD it is tun0, tun1, etcetera.
+
+# Set hardware ethernet address (required!)
+ifconfig $INTERFACE hw ether fe:fd:0:0:0:0
+
+# Give it the right ip and netmask. Remember, the subnet of the
+# tap device must be larger than that of the individual Subnets
+# as defined in the host configuration file!
+ifconfig $INTERFACE 192.168.1.1 netmask 255.255.0.0 -arp
--- /dev/null
+# Sample tinc configuration file
+
+# This is a comment.
+# Spaces and tabs are eliminated.
+# The = sign isn't strictly necessary any longer, though you may want
+# to leave it in as it improves readability :)
+# Variable names are treated case insensitive.
+
+# The name of this tinc host. Required.
+Name = alpha
+
+# The internet host to connect with.
+# Comment these out to make yourself a listen-only connection
+# You must use the name of another tinc host.
+# May be used multiple times for redundance.
+ConnectTo = beta
+
+# The tap device tinc will use. Required.
+# Default is /dev/tap0 for ethertap or FreeBSD,
+# /dev/tun0 for Solaris and OpenBSD,
+# and /dev/misc/net/tun for Linux tun/tap device.
+Device = /dev/misc/net/tun
+
+# The file in which the private key for this host is stored. Required.
+PrivateKeyFile = /etc/tinc/example/rsa_key.priv
--- /dev/null
+/*
+ avl_tree.c -- avl_ tree and linked list convenience
+ Copyright (C) 1998 Michael H. Buselli
+ 2000,2001 Ivo Timmermans <itimmermans@bigfoot.com>,
+ 2000,2001 Guus Sliepen <guus@sliepen.warande.net>
+ 2000,2001 Wessel Dankers <wsl@nl.linux.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Original AVL tree library by Michael H. Buselli <cosine@cosine.org>.
+
+ Modified 2000-11-28 by Wessel Dankers <wsl@nl.linux.org> to use counts
+ instead of depths, to add the ->next and ->prev and to generally obfuscate
+ the code. Mail me if you found a bug.
+
+ Cleaned up and incorporated some of the ideas from the red-black tree
+ library for inclusion into tinc (http://tinc.nl.linux.org/) by
+ Guus Sliepen <guus@sliepen.warande.net>.
+
+ $Id: avl_tree.c,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <xalloc.h>
+
+#include "avl_tree.h"
+
+#ifdef AVL_COUNT
+#define AVL_NODE_COUNT(n) ((n) ? (n)->count : 0)
+#define AVL_L_COUNT(n) (AVL_NODE_COUNT((n)->left))
+#define AVL_R_COUNT(n) (AVL_NODE_COUNT((n)->right))
+#define AVL_CALC_COUNT(n) (AVL_L_COUNT(n) + AVL_R_COUNT(n) + 1)
+#endif
+
+#ifdef AVL_DEPTH
+#define AVL_NODE_DEPTH(n) ((n) ? (n)->depth : 0)
+#define L_AVL_DEPTH(n) (AVL_NODE_DEPTH((n)->left))
+#define R_AVL_DEPTH(n) (AVL_NODE_DEPTH((n)->right))
+#define AVL_CALC_DEPTH(n) ((L_AVL_DEPTH(n)>R_AVL_DEPTH(n)?L_AVL_DEPTH(n):R_AVL_DEPTH(n)) + 1)
+#endif
+
+#ifndef AVL_DEPTH
+int lg(unsigned int u)
+{
+ int r = 1;
+ if (!u)
+ return 0;
+ if (u & 0xffff0000)
+ {
+ u >>= 16;
+ r += 16;
+ }
+ if (u & 0x0000ff00)
+ {
+ u >>= 8;
+ r += 8;
+ }
+ if (u & 0x000000f0)
+ {
+ u >>= 4;
+ r += 4;
+ }
+ if (u & 0x0000000c)
+ {
+ u >>= 2;
+ r += 2;
+ }
+ if (u & 0x00000002)
+ r++;
+ return r;
+}
+#endif
+
+/* Internal helper functions */
+
+int avl_check_balance(avl_node_t *node)
+{
+#ifdef AVL_DEPTH
+ int d;
+ d = R_AVL_DEPTH(node) - L_AVL_DEPTH(node);
+ return d < -1 ? -1 : d > 1 ? 1 : 0;
+#else
+/* int d;
+ * d = lg(AVL_R_COUNT(node)) - lg(AVL_L_COUNT(node));
+ * d = d<-1?-1:d>1?1:0;
+ */
+ int pl, r;
+
+ pl = lg(AVL_L_COUNT(node));
+ r = AVL_R_COUNT(node);
+
+ if (r >> pl + 1)
+ return 1;
+ if (pl < 2 || r >> pl - 2)
+ return 0;
+ return -1;
+#endif
+}
+
+void avl_rebalance(avl_tree_t *tree, avl_node_t *node)
+{
+ avl_node_t *child;
+ avl_node_t *gchild;
+ avl_node_t *parent;
+ avl_node_t **superparent;
+
+ parent = node;
+
+ while (node)
+ {
+ parent = node->parent;
+
+ superparent = parent ? node == parent->left ? &parent->left : &parent->right : &tree->root;
+
+ switch (avl_check_balance(node))
+ {
+ case -1:
+ child = node->left;
+#ifdef AVL_DEPTH
+ if(L_AVL_DEPTH(child) >= R_AVL_DEPTH(child)) {
+#else
+ if (AVL_L_COUNT(child) >= AVL_R_COUNT(child))
+ {
+#endif
+ node->left = child->right;
+ if (node->left)
+ node->left->parent = node;
+ child->right = node;
+ node->parent = child;
+ *superparent = child;
+ child->parent = parent;
+#ifdef AVL_COUNT
+ node->count = AVL_CALC_COUNT(node);
+ child->count = AVL_CALC_COUNT(child);
+#endif
+#ifdef AVL_DEPTH
+ node->depth = AVL_CALC_DEPTH(node);
+ child->depth = AVL_CALC_DEPTH(child);
+#endif
+ } else
+ {
+ gchild = child->right;
+ node->left = gchild->right;
+ if (node->left)
+ node->left->parent = node;
+ child->right = gchild->left;
+ if (child->right)
+ child->right->parent = child;
+ gchild->right = node;
+ if (gchild->right)
+ gchild->right->parent = gchild;
+ gchild->left = child;
+ if (gchild->left)
+ gchild->left->parent = gchild;
+ *superparent = gchild;
+ gchild->parent = parent;
+#ifdef AVL_COUNT
+ node->count = AVL_CALC_COUNT(node);
+ child->count = AVL_CALC_COUNT(child);
+ gchild->count = AVL_CALC_COUNT(gchild);
+#endif
+#ifdef AVL_DEPTH
+ node->depth = AVL_CALC_DEPTH(node);
+ child->depth = AVL_CALC_DEPTH(child);
+ gchild->depth = AVL_CALC_DEPTH(gchild);
+#endif
+ }
+ break;
+ case 1:
+ child = node->right;
+#ifdef AVL_DEPTH
+ if(R_AVL_DEPTH(child) >= L_AVL_DEPTH(child)) {
+#else
+ if (AVL_R_COUNT(child) >= AVL_L_COUNT(child))
+ {
+#endif
+ node->right = child->left;
+ if (node->right)
+ node->right->parent = node;
+ child->left = node;
+ node->parent = child;
+ *superparent = child;
+ child->parent = parent;
+#ifdef AVL_COUNT
+ node->count = AVL_CALC_COUNT(node);
+ child->count = AVL_CALC_COUNT(child);
+#endif
+#ifdef AVL_DEPTH
+ node->depth = AVL_CALC_DEPTH(node);
+ child->depth = AVL_CALC_DEPTH(child);
+#endif
+ } else
+ {
+ gchild = child->left;
+ node->right = gchild->left;
+ if (node->right)
+ node->right->parent = node;
+ child->left = gchild->right;
+ if (child->left)
+ child->left->parent = child;
+ gchild->left = node;
+ if (gchild->left)
+ gchild->left->parent = gchild;
+ gchild->right = child;
+ if (gchild->right)
+ gchild->right->parent = gchild;
+ *superparent = gchild;
+ gchild->parent = parent;
+#ifdef AVL_COUNT
+ node->count = AVL_CALC_COUNT(node);
+ child->count = AVL_CALC_COUNT(child);
+ gchild->count = AVL_CALC_COUNT(gchild);
+#endif
+#ifdef AVL_DEPTH
+ node->depth = AVL_CALC_DEPTH(node);
+ child->depth = AVL_CALC_DEPTH(child);
+ gchild->depth = AVL_CALC_DEPTH(gchild);
+#endif
+ }
+ break;
+ default:
+#ifdef AVL_COUNT
+ node->count = AVL_CALC_COUNT(node);
+#endif
+#ifdef AVL_DEPTH
+ node->depth = AVL_CALC_DEPTH(node);
+#endif
+ }
+ node = parent;
+ }
+}
+
+/* (De)constructors */
+
+avl_tree_t *avl_alloc_tree(avl_compare_t compare, avl_action_t delete)
+{
+ avl_tree_t *tree;
+
+ tree = xmalloc_and_zero(sizeof(avl_tree_t));
+ tree->compare = compare;
+ tree->delete = delete;
+
+ return tree;
+}
+
+void avl_free_tree(avl_tree_t *tree)
+{
+ free(tree);
+}
+
+avl_node_t *avl_alloc_node(void)
+{
+ avl_node_t *node;
+
+ node = xmalloc_and_zero(sizeof(avl_node_t));
+
+ return node;
+}
+
+void avl_free_node(avl_tree_t *tree, avl_node_t *node)
+{
+ if(node->data && tree->delete)
+ tree->delete(node->data);
+ free(node);
+}
+
+/* Searching */
+
+void *avl_search(const avl_tree_t *tree, const void *data)
+{
+ avl_node_t *node;
+
+ node = avl_search_node(tree, data);
+
+ return node?node->data:NULL;
+}
+
+void *avl_search_closest(const avl_tree_t *tree, const void *data, int *result)
+{
+ avl_node_t *node;
+
+ node = avl_search_closest_node(tree, data, result);
+
+ return node?node->data:NULL;
+}
+
+void *avl_search_closest_smaller(const avl_tree_t *tree, const void *data)
+{
+ avl_node_t *node;
+
+ node = avl_search_closest_smaller_node(tree, data);
+
+ return node?node->data:NULL;
+}
+
+void *avl_search_closest_greater(const avl_tree_t *tree, const void *data)
+{
+ avl_node_t *node;
+
+ node = avl_search_closest_greater_node(tree, data);
+
+ return node?node->data:NULL;
+}
+
+avl_node_t *avl_search_node(const avl_tree_t *tree, const void *data)
+{
+ avl_node_t *node;
+ int result;
+
+ node = avl_search_closest_node(tree, data, &result);
+
+ return result?NULL:node;
+}
+
+avl_node_t *avl_search_closest_node(const avl_tree_t *tree, const void *data, int *result)
+{
+ avl_node_t *node;
+ int c;
+
+ node = tree->root;
+
+ if (!node)
+ {
+ if(result)
+ *result = 0;
+ return NULL;
+ }
+
+ for (;;)
+ {
+ c = tree->compare(data, node->data);
+
+ if (c < 0)
+ {
+ if (node->left)
+ node = node->left;
+ else
+ {
+ if(result)
+ *result = -1;
+ break;
+ }
+ }
+ else if (c > 0)
+ {
+ if (node->right)
+ node = node->right;
+ else
+ {
+ if(result)
+ *result = 1;
+ break;
+ }
+ }
+ else
+ {
+ if(result)
+ *result = 0;
+ break;
+ }
+ }
+
+ return node;
+}
+
+avl_node_t *avl_search_closest_smaller_node(const avl_tree_t *tree, const void *data)
+{
+ avl_node_t *node;
+ int result;
+
+ node = avl_search_closest_node(tree, data, &result);
+
+ if(result < 0)
+ node = node->prev;
+
+ return node;
+}
+
+avl_node_t *avl_search_closest_greater_node(const avl_tree_t *tree, const void *data)
+{
+ avl_node_t *node;
+ int result;
+
+ node = avl_search_closest_node(tree, data, &result);
+
+ if(result > 0)
+ node = node->next;
+
+ return node;
+}
+
+/* Insertion and deletion */
+
+avl_node_t *avl_insert(avl_tree_t *tree, void *data)
+{
+ avl_node_t *closest, *new;
+ int result;
+
+ if (!tree->root)
+ {
+ new = avl_alloc_node();
+ new->data = data;
+ avl_insert_top(tree, new);
+ }
+ else
+ {
+ closest = avl_search_closest_node(tree, data, &result);
+ switch(result)
+ {
+ case -1:
+ new = avl_alloc_node();
+ new->data = data;
+ avl_insert_before(tree, closest, new);
+ break;
+ case 1:
+ new = avl_alloc_node();
+ new->data = data;
+ avl_insert_after(tree, closest, new);
+ break;
+ default:
+ return NULL;
+ }
+ }
+
+#ifdef AVL_COUNT
+ new->count = 1;
+#endif
+#ifdef AVL_DEPTH
+ new->depth = 1;
+#endif
+
+ return new;
+}
+
+avl_node_t *avl_insert_node(avl_tree_t *tree, avl_node_t *node)
+{
+ avl_node_t *closest;
+ int result;
+
+ if (!tree->root)
+ avl_insert_top(tree, node);
+ else
+ {
+ closest = avl_search_closest_node(tree, node->data, &result);
+ switch(result)
+ {
+ case -1:
+ avl_insert_before(tree, closest, node);
+ break;
+ case 1:
+ avl_insert_after(tree, closest, node);
+ break;
+ case 0:
+ return NULL;
+ }
+ }
+
+#ifdef AVL_COUNT
+ node->count = 1;
+#endif
+#ifdef AVL_DEPTH
+ node->depth = 1;
+#endif
+
+ return node;
+}
+
+void avl_insert_top(avl_tree_t *tree, avl_node_t *node)
+{
+ node->prev = node->next = node->parent = NULL;
+ tree->head = tree->tail = tree->root = node;
+}
+
+void avl_insert_before(avl_tree_t *tree, avl_node_t *before, avl_node_t *node)
+{
+ if (!before)
+ return tree->tail ? avl_insert_after(tree, tree->tail, node) : avl_insert_top(tree, node);
+
+ node->next = before;
+ node->parent = before;
+ node->prev = before->prev;
+
+ if(before->left)
+ return avl_insert_after(tree, before->prev, node);
+
+ if (before->prev)
+ before->prev->next = node;
+ else
+ tree->head = node;
+
+ before->prev = node;
+ before->left = node;
+
+ avl_rebalance(tree, before->parent);
+}
+
+void avl_insert_after(avl_tree_t *tree, avl_node_t *after, avl_node_t *node)
+{
+ if (!after)
+ return tree->head ? avl_insert_before(tree, tree->head, node) : avl_insert_top(tree, node);
+
+ if(after->right)
+ return avl_insert_before(tree, after->next, node);
+
+ node->prev = after;
+ node->parent = after;
+ node->next = after->next;
+
+ if (after->next)
+ after->next->prev = node;
+ else
+ tree->tail = node;
+
+ after->next = node;
+ after->right = node;
+
+ avl_rebalance(tree, after->parent);
+}
+
+avl_node_t *avl_unlink(avl_tree_t *tree, void *data)
+{
+ avl_node_t *node;
+
+ node = avl_search_node(tree, data);
+
+ if(node)
+ avl_unlink_node(tree, node);
+
+ return node;
+}
+
+void avl_unlink_node(avl_tree_t *tree, avl_node_t *node)
+{
+ avl_node_t *parent;
+ avl_node_t **superparent;
+ avl_node_t *subst, *left, *right;
+ avl_node_t *balnode;
+
+ if (node->prev)
+ node->prev->next = node->next;
+ else
+ tree->head = node->next;
+ if (node->next)
+ node->next->prev = node->prev;
+ else
+ tree->tail = node->prev;
+
+ parent = node->parent;
+
+ superparent = parent ? node == parent->left ? &parent->left : &parent->right : &tree->root;
+
+ left = node->left;
+ right = node->right;
+ if (!left)
+ {
+ *superparent = right;
+ if (right)
+ right->parent = parent;
+ balnode = parent;
+ } else if (!right)
+ {
+ *superparent = left;
+ left->parent = parent;
+ balnode = parent;
+ } else
+ {
+ subst = node->prev;
+ if (subst == left)
+ {
+ balnode = subst;
+ } else
+ {
+ balnode = subst->parent;
+ balnode->right = subst->left;
+ if (balnode->right)
+ balnode->right->parent = balnode;
+ subst->left = left;
+ left->parent = subst;
+ }
+ subst->right = right;
+ subst->parent = parent;
+ right->parent = subst;
+ *superparent = subst;
+ }
+
+ avl_rebalance(tree, balnode);
+
+ node->next = node->prev = node->parent = node->left = node->right = NULL;
+
+#ifdef AVL_COUNT
+ node->count = 0;
+#endif
+#ifdef AVL_DEPTH
+ node->depth = 0;
+#endif
+}
+
+void avl_delete_node(avl_tree_t *tree, avl_node_t *node)
+{
+ avl_unlink_node(tree, node);
+ avl_free_node(tree, node);
+}
+
+void avl_delete(avl_tree_t *tree, void *data)
+{
+ avl_node_t *node;
+
+ node = avl_search_node(tree, data);
+
+ if (node)
+ avl_delete_node(tree, node);
+}
+
+/* Fast tree cleanup */
+
+void avl_delete_tree(avl_tree_t *tree)
+{
+ avl_node_t *node, *next;
+
+ for(node = tree->root; node; node = next)
+ {
+ next = node->next;
+ avl_free_node(tree, node);
+ }
+
+ avl_free_tree(tree);
+}
+
+/* Tree walking */
+
+void avl_foreach(avl_tree_t *tree, avl_action_t action)
+{
+ avl_node_t *node, *next;
+
+ for(node = tree->head; node; node = next)
+ {
+ next = node->next;
+ action(node->data);
+ }
+}
+
+void avl_foreach_node(avl_tree_t *tree, avl_action_t action)
+{
+ avl_node_t *node, *next;
+
+ for(node = tree->head; node; node = next)
+ {
+ next = node->next;
+ action(node);
+ }
+}
+
+/* Indexing */
+
+#ifdef AVL_COUNT
+unsigned int avl_count(avl_tree_t *tree)
+{
+ return AVL_NODE_COUNT(tree->root);
+}
+
+avl_node_t *avl_get_node(const avl_tree_t *tree, unsigned int index)
+{
+ avl_node_t *node;
+ unsigned int c;
+
+ node = tree->root;
+
+ while (node)
+ {
+ c = AVL_L_COUNT(node);
+
+ if (index < c)
+ {
+ node = node->left;
+ } else if (index > c)
+ {
+ node = node->right;
+ index -= c + 1;
+ } else
+ {
+ return node;
+ }
+ }
+
+ return NULL;
+}
+
+unsigned int avl_index(const avl_node_t *node)
+{
+ avl_node_t *next;
+ unsigned int index;
+
+ index = AVL_L_COUNT(node);
+
+ while ((next = node->parent))
+ {
+ if (node == next->right)
+ index += AVL_L_COUNT(next) + 1;
+ node = next;
+ }
+
+ return index;
+}
+#endif
+#ifdef AVL_DEPTH
+unsigned int avl_depth(avl_tree_t *tree)
+{
+ return AVL_NODE_DEPTH(tree->root);
+}
+#endif
--- /dev/null
+/*
+ avl_tree.h -- header file for avl_tree.c
+ Copyright (C) 1998 Michael H. Buselli
+ 2000,2001 Ivo Timmermans <itimmermans@bigfoot.com>,
+ 2000,2001 Guus Sliepen <guus@sliepen.warande.net>
+ 2000,2001 Wessel Dankers <wsl@nl.linux.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Original AVL tree library by Michael H. Buselli <cosine@cosine.org>.
+
+ Modified 2000-11-28 by Wessel Dankers <wsl@nl.linux.org> to use counts
+ instead of depths, to add the ->next and ->prev and to generally obfuscate
+ the code. Mail me if you found a bug.
+
+ Cleaned up and incorporated some of the ideas from the red-black tree
+ library for inclusion into tinc (http://tinc.nl.linux.org/) by
+ Guus Sliepen <guus@sliepen.warande.net>.
+
+ $Id: avl_tree.h,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+
+#ifndef __AVL_TREE_H__
+#define __AVL_TREE_H__
+
+#ifndef AVL_DEPTH
+ #ifndef AVL_COUNT
+ #define AVL_DEPTH
+ #endif
+#endif
+
+typedef struct avl_node_t {
+
+ /* Linked list part */
+
+ struct avl_node_t *next;
+ struct avl_node_t *prev;
+
+ /* Tree part */
+
+ struct avl_node_t *parent;
+ struct avl_node_t *left;
+ struct avl_node_t *right;
+
+#ifdef AVL_COUNT
+ unsigned int count;
+#endif
+#ifdef AVL_DEPTH
+ unsigned char depth;
+#endif
+
+ /* Payload */
+
+ void *data;
+
+} avl_node_t;
+
+typedef int (*avl_compare_t) (const void *, const void *);
+typedef void (*avl_action_t) (const void *);
+typedef void (*avl_action_node_t) (const avl_node_t *);
+
+typedef struct avl_tree_t {
+
+ /* Linked list part */
+
+ avl_node_t *head;
+ avl_node_t *tail;
+
+ /* Tree part */
+
+ avl_node_t *root;
+
+ avl_compare_t compare;
+ avl_action_t delete;
+
+} avl_tree_t;
+
+/* (De)constructors */
+
+extern avl_tree_t *avl_alloc_tree(avl_compare_t, avl_action_t);
+extern void avl_free_tree(avl_tree_t *);
+
+extern avl_node_t *avl_alloc_node(void);
+extern void avl_free_node(avl_tree_t *tree, avl_node_t *);
+
+/* Insertion and deletion */
+
+extern avl_node_t *avl_insert(avl_tree_t *, void *);
+extern avl_node_t *avl_insert_node(avl_tree_t *, avl_node_t *);
+
+extern void avl_insert_top(avl_tree_t *, avl_node_t *);
+extern void avl_insert_before(avl_tree_t *, avl_node_t *, avl_node_t *);
+extern void avl_insert_after(avl_tree_t *, avl_node_t *, avl_node_t *);
+
+extern avl_node_t *avl_unlink(avl_tree_t *, void *);
+extern void avl_unlink_node(avl_tree_t *tree, avl_node_t *);
+extern void avl_delete(avl_tree_t *, void *);
+extern void avl_delete_node(avl_tree_t *, avl_node_t *);
+
+/* Fast tree cleanup */
+
+extern void avl_delete_tree(avl_tree_t *);
+
+/* Searching */
+
+extern void *avl_search(const avl_tree_t *, const void *);
+extern void *avl_search_closest(const avl_tree_t *, const void *, int *);
+extern void *avl_search_closest_smaller(const avl_tree_t *, const void *);
+extern void *avl_search_closest_greater(const avl_tree_t *, const void *);
+
+extern avl_node_t *avl_search_node(const avl_tree_t *, const void *);
+extern avl_node_t *avl_search_closest_node(const avl_tree_t *, const void *, int *);
+extern avl_node_t *avl_search_closest_smaller_node(const avl_tree_t *, const void *);
+extern avl_node_t *avl_search_closest_greater_node(const avl_tree_t *, const void *);
+
+/* Tree walking */
+
+extern void avl_foreach(avl_tree_t *, avl_action_t);
+extern void avl_foreach_node(avl_tree_t *, avl_action_t);
+
+/* Indexing */
+
+#ifdef AVL_COUNT
+extern unsigned int avl_count(avl_tree_t *);
+extern avl_node_t *avl_get_node(const avl_tree_t *, unsigned int);
+extern unsigned int avl_index(const avl_node_t *);
+#endif
+#ifdef AVL_DEPTH
+extern unsigned int avl_depth(avl_tree_t *);
+#endif
+
+#endif /* __AVL_TREE_H__ */
--- /dev/null
+/*
+ dropin.c -- a set of drop-in replacements for libc functions
+ Copyright (C) 2000,2001 Ivo Timmermans <itimmermans@bigfoot.com>,
+ 2000,2001 Guus Sliepen <guus@sliepen.warande.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: dropin.c,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include <xalloc.h>
+
+#include <system.h>
+#include <errno.h>
+
+#ifndef HAVE_DAEMON
+/*
+ Replacement for the daemon() function.
+
+ The daemon() function is for programs wishing to detach themselves
+ from the controlling terminal and run in the background as system
+ daemons.
+
+ Unless the argument nochdir is non-zero, daemon() changes the
+ current working directory to the root (``/'').
+
+ Unless the argument noclose is non-zero, daemon() will redirect
+ standard input, standard output and standard error to /dev/null.
+*/
+int daemon(int nochdir, int noclose)
+{
+ pid_t pid;
+ int fd;
+
+ pid = fork();
+
+ /* Check if forking failed */
+ if(pid < 0)
+ {
+ perror("fork");
+ exit(-1);
+ }
+
+ /* If we are the parent, terminate */
+ if(pid)
+ exit(0);
+
+ /* Detach by becoming the new process group leader */
+ if(setsid() < 0)
+ {
+ perror("setsid");
+ return -1;
+ }
+
+ /* Change working directory to the root (to avoid keeping mount
+ points busy) */
+ if(!nochdir)
+ {
+ chdir("/");
+ }
+
+ /* Redirect stdin/out/err to /dev/null */
+ if(!noclose)
+ {
+ fd = open("/dev/null", O_RDWR);
+
+ if(fd < 0)
+ {
+ perror("opening /dev/null");
+ return -1;
+ }
+ else
+ {
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ }
+ }
+
+ return 0;
+}
+#endif
+
+
+
+
+#ifndef HAVE_GET_CURRENT_DIR_NAME
+/*
+ Replacement for the GNU get_current_dir_name function:
+
+ get_current_dir_name will malloc(3) an array big enough to hold the
+ current directory name. If the environment variable PWD is set, and
+ its value is correct, then that value will be returned.
+*/
+char *get_current_dir_name(void)
+{
+ size_t size;
+ char *buf;
+ char *r;
+
+ /* Start with 100 bytes. If this turns out to be insufficient to
+ contain the working directory, double the size. */
+ size = 100;
+ buf = xmalloc(size);
+
+ errno = 0; /* Success */
+ r = getcwd(buf, size);
+ /* getcwd returns NULL and sets errno to ERANGE if the bufferspace
+ is insufficient to contain the entire working directory. */
+ while(r == NULL && errno == ERANGE)
+ {
+ free(buf);
+ size <<= 1; /* double the size */
+ buf = xmalloc(size);
+ r = getcwd(buf, size);
+ }
+
+ return buf;
+}
+#endif
+
+#ifndef HAVE_ASPRINTF
+int asprintf(char **buf, const char *fmt, ...)
+{
+ int status;
+ va_list ap;
+ int len;
+
+ len = 4096;
+ *buf = xmalloc(len);
+
+ va_start(ap, fmt);
+ status = vsnprintf (*buf, len, fmt, ap);
+ va_end (ap);
+
+ if(status >= 0)
+ *buf = xrealloc(*buf, status);
+
+ if(status > len-1)
+ {
+ len = status;
+ va_start(ap, fmt);
+ status = vsnprintf (*buf, len, fmt, ap);
+ va_end (ap);
+ }
+
+ return status;
+}
+#endif
--- /dev/null
+/*
+ dropin.h -- header file for dropin.c
+ Copyright (C) 2000,2001 Ivo Timmermans <itimmermans@bigfoot.com>,
+ 2000,2001 Guus Sliepen <guus@sliepen.warande.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: dropin.h,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+#ifndef __DROPIN_H__
+#define __DROPIN_H__
+
+#ifndef HAVE_DAEMON
+extern int daemon(int, int);
+#endif
+
+#ifndef HAVE_GET_CURRENT_DIR_NAME
+extern char* get_current_dir_name(void);
+#endif
+
+#ifndef HAVE_ASPRINTF
+extern int asprintf(char **, const char *, ...);
+#endif
+
+#endif /* __DROPIN_H__ */
/*
list.c -- functions to deal with double linked lists
- Copyright (C) 2000 Ivo Timmermans <itimmermans@bigfoot.com>
- 2000 Guus Sliepen <guus@sliepen.warande.net>
+ Copyright (C) 2000,2001 Ivo Timmermans <itimmermans@bigfoot.com>
+ 2000,2001 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- $Id: list.c,v 1.1 2000/10/20 16:44:32 zarq Exp $
+ $Id: list.c,v 1.2 2002/04/09 15:26:00 zarq Exp $
*/
#include "config.h"
-#include <string.h>
+#include <stdlib.h>
-#include <error.h>
-#include <list.h>
#include <xalloc.h>
-
#include <system.h>
-/*
- list_new
+#include "list.h"
- Initialize a new list.
-*/
-list_t *list_new(void)
+/* (De)constructors */
+
+list_t *list_alloc(list_action_t delete)
{
list_t *list;
list = xmalloc_and_zero(sizeof(list_t));
+ list->delete = delete;
+
return list;
}
-/*
- list_delete
+void list_free(list_t *list)
+{
+ free(list);
+}
- Delete the element pointed to by idx from the list.
-*/
-list_node_t *list_delete(list_t *list, list_node_t *idx)
+list_node_t *list_alloc_node(void)
{
- list_node_t *res;
+ list_node_t *node;
- if(!list)
- return NULL;
- if(!idx)
- return NULL;
+ node = xmalloc_and_zero(sizeof(list_node_t));
+
+ return node;
+}
- if(list->callbacks->delete != NULL)
- if(list->callbacks->delete(idx->data))
- error(ERR_WARNING, N_("List callback[delete] failed for %08lx - freeing anyway"), idx->data);
+void list_free_node(list_t *list, list_node_t *node)
+{
+ if(node->data && list->delete)
+ list->delete(node->data);
- free(idx->data);
+ free(node);
+}
+
+/* Insertion and deletion */
+
+list_node_t *list_insert_head(list_t *list, void *data)
+{
+ list_node_t *node;
- if(idx->prev == NULL)
- /* First element in list */
- {
- res = idx->next;
- list->head = idx->next;
- }
- if(idx->next == NULL)
- /* Last element in list */
- {
- res = NULL;
- list->tail = idx->prev;
- }
- if(idx->prev != NULL && idx->next != NULL)
- /* Neither first nor last element */
- {
- idx->prev->next = idx->next;
- idx->next->prev = idx->prev;
- }
- if(list->head == NULL)
- list->tail = NULL;
+ node = list_alloc_node();
+
+ node->data = data;
+ node->prev = NULL;
+ node->next = list->head;
+ list->head = node;
+
+ if(node->next)
+ node->next->prev = node;
else
- if(list->tail == NULL)
- list->head = NULL;
- free(idx);
- return res;
+ list->tail = node;
+
+ list->count++;
+
+ return node;
}
-/*
- list_forall_nodes
+list_node_t *list_insert_tail(list_t *list, void *data)
+{
+ list_node_t *node;
+
+ node = list_alloc_node();
+
+ node->data = data;
+ node->next = NULL;
+ node->prev = list->tail;
+ list->tail = node;
+
+ if(node->prev)
+ node->prev->next = node;
+ else
+ list->head = node;
- Call function() on each element in the list. If this function
- returns non-zero, the element will be removed from the list.
-*/
-void list_forall_nodes(list_t *list, int (*function)(void *data))
+ list->count++;
+
+ return node;
+}
+
+void list_unlink_node(list_t *list, list_node_t *node)
{
- list_node_t *p;
- int res;
+ if(node->prev)
+ node->prev->next = node->next;
+ else
+ list->head = node->next;
+
+ if(node->next)
+ node->next->prev = node->prev;
+ else
+ list->tail = node->prev;
+
+ list->count--;
+}
+
+void list_delete_node(list_t *list, list_node_t *node)
+{
+ list_unlink_node(list, node);
+ list_free_node(list, node);
+}
+
+void list_delete_head(list_t *list)
+{
+ list_delete_node(list, list->head);
+}
+
+void list_delete_tail(list_t *list)
+{
+ list_delete_node(list, list->tail);
+}
+
+/* Head/tail lookup */
+
+void *list_get_head(list_t *list)
+{
+ if(list->head)
+ return list->head->data;
+ else
+ return NULL;
+}
+
+void *list_get_tail(list_t *list)
+{
+ if(list->tail)
+ return list->tail->data;
+ else
+ return NULL;
+}
+
+/* Fast list deletion */
+
+void list_delete_list(list_t *list)
+{
+ list_node_t *node, *next;
- if(!list) /* no list given */
- return;
- if(!function) /* no function given */
- return;
- if(!list->head) /* list is empty */
- return;
- for(p = list->head; p != NULL; p = p->next)
+ for(node = list->head; node; node = next)
{
- res = function(p->data);
- if(res != 0)
- p = list_delete(list, p);
+ next = node->next;
+ list_free_node(list, node);
}
+
+ list_free(list);
}
-/*
- list_destroy
+/* Traversing */
- Free all datastructures contained in this list. It uses the delete
- callback for this list to free each element.
-*/
-void list_destroy(list_t *list)
+void list_foreach_node(list_t *list, list_action_node_t action)
{
- if(!list)
- return;
- list_destroy_nodes(list);
- free(list);
-}
+ list_node_t *node, *next;
-/*
- list_append
+ for(node = list->head; node; node = next)
+ {
+ next = node->next;
+ action(node);
+ }
+}
- Append a new node to the list that points to data.
-*/
-list_append(list_t *list, void *data)
+void list_foreach(list_t *list, list_action_t action)
{
- list_node_t *n;
+ list_node_t *node, *next;
- n = xmalloc_and_zero(sizeof(list_node_t));
- n->data = data;
- n->prev = list->tail;
- list->tail->next = n;
- list->tail = n;
+ for(node = list->head; node; node = next)
+ {
+ next = node->next;
+ if(node->data)
+ action(node->data);
+ }
}
/*
list.h -- header file for list.c
- Copyright (C) 2000 Ivo Timmermans <itimmermans@bigfoot.com>
- 2000 Guus Sliepen <guus@sliepen.warande.net>
+ Copyright (C) 2000,2001 Ivo Timmermans <itimmermans@bigfoot.com>
+ 2000,2001 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- $Id: list.h,v 1.1 2000/10/20 16:44:32 zarq Exp $
+ $Id: list.h,v 1.2 2002/04/09 15:26:00 zarq Exp $
*/
#ifndef __TINC_LIST_H__
#define __TINC_LIST_H__
-typedef struct list_callbacks_t {
- int (*delete) (void *);
-} list_callbacks_t;
-
-typedef struct list_node_t {
- void *data;
+typedef struct list_node_t
+{
struct list_node_t *prev;
struct list_node_t *next;
+
+ /* Payload */
+
+ void *data;
} list_node_t;
-typedef struct list_t {
+typedef void (*list_action_t) (const void *);
+typedef void (*list_action_node_t) (const list_node_t *);
+
+typedef struct list_t
+{
list_node_t *head;
list_node_t *tail;
- list_callbacks_t *callbacks;
+ int count;
+
+ /* Callbacks */
+
+ list_action_t delete;
} list_t;
+/* (De)constructors */
+
+extern list_t *list_alloc(list_action_t);
+extern void list_free(list_t *);
+extern list_node_t *list_alloc_node(void);
+extern void list_free_node(list_t *, list_node_t *);
+
+/* Insertion and deletion */
+
+extern list_node_t *list_insert_head(list_t *, void *);
+extern list_node_t *list_insert_tail(list_t *, void *);
+
+extern void list_unlink_node(list_t *, list_node_t *);
+extern void list_delete_node(list_t *, list_node_t *);
+
+extern void list_delete_head(list_t *);
+extern void list_delete_tail(list_t *);
+
+/* Head/tail lookup */
+
+extern void *list_get_head(list_t *);
+extern void *list_get_tail(list_t *);
+
+/* Fast list deletion */
+
+extern void list_delete_list(list_t *);
+
+/* Traversing */
+extern void list_foreach(list_t *, list_action_t);
+extern void list_foreach_node(list_t *, list_action_node_t);
#endif /* __TINC_LIST_H__ */
#include <string.h>
#include <errno.h>
#include <signal.h>
+#include <sys/types.h>
+#include <fcntl.h>
/* read_pid
*
* be found -- GW
*/
/* But... errno is usually changed only on error.. */
+ errno = 0;
if (kill(pid, 0) && errno == ESRCH)
return(0);
fprintf(stderr, "Can't open or create %s.\n", pidfile);
return 0;
}
-
+
+#ifdef HAVE_FLOCK
if (flock(fd, LOCK_EX|LOCK_NB) == -1) {
fscanf(f, "%d", &pid);
fclose(f);
printf("Can't lock, lock is held by pid %d.\n", pid);
return 0;
}
+#endif
pid = getpid();
if (!fprintf(f,"%d\n", pid)) {
}
fflush(f);
+#ifdef HAVE_FLOCK
if (flock(fd, LOCK_UN) == -1) {
printf("Can't unlock pidfile %s, %s.\n", pidfile, strerror(errno));
close(fd);
return 0;
}
+#endif
close(fd);
return pid;
--- /dev/null
+/*
+ rbl.c -- red-black tree + linked list convenience
+ Copyright (C) 2000 Ivo Timmermans <itimmermans@bigfoot.com>,
+ 2000 Guus Sliepen <guus@sliepen.warande.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: rbl.c,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <xalloc.h>
+
+#include "rbl.h"
+#include <system.h>
+
+/* Allocate a new rbl node */
+rbl_t *new_rbl()
+{
+ return (rbl_t *)xmalloc_and_zero(sizeof(rbl_t));
+}
+
+/* Free a rbl node */
+void free_rbl(rbl_t *rbl)
+{
+ if(rbl->data && rbl->tree->delete)
+ rbl->tree->delete(rbl->data);
+ free(rbl);
+}
+
+/* Allocate a new rbltree header */
+rbltree_t *new_rbltree(rbl_compare_t compare, rbl_action_t delete)
+{
+ rbltree_t *tree;
+
+ tree = (rbltree_t *)xmalloc_and_zero(sizeof(rbltree_t));
+ if(tree)
+ {
+ tree->compare = compare;
+ tree->delete = delete;
+ }
+
+ return tree;
+}
+
+/* Free a rbltree header */
+void free_rbltree(rbltree_t *tree)
+{
+ free(tree);
+}
+
+/* Search closest match in the tree */
+rbl_t *rbl_search_closest_rbl(rbltree_t *tree, void *data)
+{
+ rbl_t *rbl, *next;
+ int result;
+
+ next = rbl = tree->top;
+
+ while(next)
+ {
+ rbl = next;
+
+ result = tree->compare(data, rbl->data);
+
+ if(result < 0)
+ next = rbl->left;
+ else if(result > 0)
+ next = rbl->right;
+ else
+ break;
+ }
+
+ return rbl;
+}
+
+/* Search closest match in the tree */
+rbl_t *rbl_search_closest_greater_rbl(rbltree_t *tree, void *data)
+{
+ rbl_t *rbl;
+
+ rbl = rbl_search_closest_rbl(tree, data);
+
+ if(rbl)
+ {
+ if(tree->compare(data, rbl->data) > 0)
+ rbl = rbl->next;
+ }
+
+ return rbl;
+}
+
+/* Search closest match in the tree */
+rbl_t *rbl_search_closest_smaller_rbl(rbltree_t *tree, void *data)
+{
+ rbl_t *rbl;
+
+ rbl = rbl_search_closest_rbl(tree, data);
+
+ if(rbl)
+ {
+ if(tree->compare(data, rbl->data) < 0)
+ rbl = rbl->next;
+ }
+
+ return rbl;
+}
+
+void *rbl_search_closest(rbltree_t *tree, void *data)
+{
+ rbl_t *rbl;
+
+ rbl = rbl_search_closest_rbl(tree, data);
+
+ if(rbl)
+ return rbl->data;
+ else
+ return NULL;
+}
+
+void *rbl_search_closest_greater(rbltree_t *tree, void *data)
+{
+ rbl_t *rbl;
+
+ rbl = rbl_search_closest_greater_rbl(tree, data);
+
+ if(rbl)
+ return rbl->data;
+ else
+ return NULL;
+}
+
+void *rbl_search_closest_smaller(rbltree_t *tree, void *data)
+{
+ rbl_t *rbl;
+
+ rbl = rbl_search_closest_smaller_rbl(tree, data);
+
+ if(rbl)
+ return rbl->data;
+ else
+ return NULL;
+}
+
+/* Search exact match or return NULL pointer */
+rbl_t *rbl_search_rbl(rbltree_t *tree, void *data)
+{
+ rbl_t *rbl;
+ int result;
+
+ rbl = tree->top;
+
+ while(rbl)
+ {
+ result = tree->compare(data, rbl->data);
+
+ if(result < 0)
+ rbl = rbl->left;
+ else if(result > 0)
+ rbl = rbl->right;
+ else
+ return rbl;
+ }
+
+ return NULL;
+}
+
+void *rbl_search(rbltree_t *tree, void *data)
+{
+ rbl_t *rbl;
+
+ rbl = rbl_search_rbl(tree, data);
+
+ if(rbl)
+ return rbl->data;
+ else
+ return NULL;
+}
+
+/* Red-black tree operations taken from Introduction to Algorithms,
+ Cormen, Leiserson & Rivest, chapter 14.
+*/
+
+void rbl_left_rotate(rbl_t *x)
+{
+ rbl_t *y;
+
+ y = x->right;
+ x->right = y->left;
+
+ if(y->left)
+ y->left->parent = x;
+
+ y->parent = x->parent;
+
+ if(!x->parent)
+ x->tree->top = y;
+ else
+ if(x == x->parent->left)
+ x->parent->left = y;
+ else
+ x->parent->right = y;
+
+ y->left = x;
+ x->parent = y;
+}
+
+void rbl_right_rotate(rbl_t *y)
+{
+ rbl_t *x;
+
+ x = y->left;
+ y->left = x->right;
+
+ if(x->right)
+ x->right->parent = y;
+
+ x->parent = y->parent;
+
+ if(!y->parent)
+ y->tree->top = x;
+ else
+ if(y == y->parent->right)
+ y->parent->right = x;
+ else
+ y->parent->left = x;
+
+ x->right = y;
+ y->parent = x;
+}
+
+/* Insert a node into the rbl tree */
+rbl_t *rbl_insert_rbl(rbltree_t *tree, rbl_t *rbl)
+{
+ rbl_t *closest, *x, *y;
+ int result;
+
+ rbl->tree = tree;
+
+ /* Binary tree and linked list insert */
+
+ if(tree->top)
+ {
+ closest = rbl_search_closest_rbl(tree, rbl->data);
+ result = tree->compare(rbl->data, closest->data);
+ if(result < 0)
+ {
+ closest->left = rbl;
+
+ rbl->prev = closest->prev;
+ rbl->next = closest;
+ closest->prev = rbl;
+
+ if(rbl->prev)
+ rbl->prev->next = rbl;
+ else
+ tree->head = rbl;
+ }
+ else if(result > 0)
+ {
+ closest->right = rbl;
+
+ rbl->next = closest->next;
+ rbl->prev = closest;
+ closest->next = rbl;
+
+ if(rbl->next)
+ rbl->next->prev = rbl;
+ else
+ tree->tail = rbl;
+ }
+ else
+ return closest; /* Ofcourse, we cannot add two identical things */
+
+ rbl->parent = closest;
+ }
+ else
+ {
+ tree->top = rbl;
+ tree->head = rbl;
+ tree->tail = rbl;
+ }
+
+ /* Red-black part of insert */
+
+ x = rbl;
+ x->color = RBL_RED;
+
+ while(x != tree->top && x->parent->color == RBL_RED)
+ {
+ if(x->parent == x->parent->parent->left)
+ {
+ y = x->parent->parent->right;
+ if(y && y->color == RBL_RED)
+ {
+ x->parent->color = RBL_BLACK;
+ y->color = RBL_BLACK;
+ x->parent->parent->color = RBL_RED;
+ x = x->parent->parent;
+ }
+ else
+ {
+ if(x == x->parent->right)
+ {
+ x = x->parent;
+ rbl_left_rotate(x);
+ }
+ x->parent->color = RBL_BLACK;
+ x->parent->parent->color = RBL_RED;
+ rbl_right_rotate(x->parent->parent);
+ }
+ }
+ else
+ {
+ y = x->parent->parent->left;
+ if(y && y->color == RBL_RED)
+ {
+ x->parent->color = RBL_BLACK;
+ y->color = RBL_BLACK;
+ x->parent->parent->color = RBL_RED;
+ x = x->parent->parent;
+ }
+ else
+ {
+ if(x == x->parent->left)
+ {
+ x = x->parent;
+ rbl_right_rotate(x);
+ }
+ x->parent->color = RBL_BLACK;
+ x->parent->parent->color = RBL_RED;
+ rbl_left_rotate(x->parent->parent);
+ }
+ }
+ }
+
+ tree->top->color = RBL_BLACK;
+ return rbl;
+}
+
+/* Create a new node and insert it into the tree */
+rbl_t *rbl_insert(rbltree_t *tree, void *data)
+{
+ rbl_t *rbl;
+
+ rbl = new_rbl();
+ rbl->data = data;
+
+ if(rbl_insert_rbl(tree, rbl) == rbl)
+ return rbl;
+ else
+ {
+ free_rbl(rbl);
+ return NULL;
+ }
+}
+
+/* Restore red-black property after violation due to a deletion */
+void rbl_delete_fixup(rbl_t *x)
+{
+ rbl_t *w;
+
+ while(x != x->tree->top && x->color == RBL_BLACK)
+ {
+ if(x == x->parent->left)
+ {
+ w = x->parent->right;
+ if(w->color == RBL_RED)
+ {
+ w->color = RBL_BLACK;
+ x->parent->color = RBL_RED;
+ rbl_left_rotate(x->parent);
+ w = x->parent->right;
+ }
+ if(w->left->color == RBL_BLACK && w->right->color == RBL_BLACK)
+ {
+ w->color = RBL_RED;
+ x = x->parent;
+ }
+ else
+ {
+ if(w->right->color == RBL_BLACK)
+ {
+ w->left->color = RBL_BLACK;
+ w->color = RBL_RED;
+ rbl_right_rotate(w);
+ w = x->parent->right;
+ }
+ w->color = x->parent->color;
+ x->parent->color = RBL_BLACK;
+ w->right->color = RBL_BLACK;
+ rbl_left_rotate(x->parent);
+ x = x->tree->top;
+ }
+ }
+ else
+ {
+ w = x->parent->left;
+ if(w->color == RBL_RED)
+ {
+ w->color = RBL_BLACK;
+ x->parent->color = RBL_RED;
+ rbl_right_rotate(x->parent);
+ w = x->parent->left;
+ }
+ if(w->right->color == RBL_BLACK && w->left->color == RBL_BLACK)
+ {
+ w->color = RBL_RED;
+ x = x->parent;
+ }
+ else
+ {
+ if(w->left->color == RBL_BLACK)
+ {
+ w->right->color = RBL_BLACK;
+ w->color = RBL_RED;
+ rbl_left_rotate(w);
+ w = x->parent->left;
+ }
+ w->color = x->parent->color;
+ x->parent->color = RBL_BLACK;
+ w->left->color = RBL_BLACK;
+ rbl_right_rotate(x->parent);
+ x = x->tree->top;
+ }
+ }
+ }
+
+ x->color = RBL_BLACK;
+}
+
+/* Unlink node from the tree, but keep the node intact. */
+rbl_t *rbl_unlink_rbl(rbl_t *rbl)
+{
+ rbl_t *x, *y;
+
+ /* Binary tree delete */
+
+ if(rbl->left && rbl->right)
+ y = rbl->next;
+ else
+ y = rbl;
+
+ if(y->left)
+ x = y->left;
+ else
+ x = y->right;
+
+ if(x)
+ x->parent = y->parent;
+
+ if(!y->parent)
+ rbl->tree->top = x;
+ else
+ if(y == y->parent->left)
+ y->parent->left = x;
+ else
+ y->parent->right = x;
+
+ if(y != rbl)
+ {
+ y->left = rbl->left;
+ y->right = rbl->right;
+ y->parent = rbl->parent;
+ if(rbl == rbl->parent->left)
+ rbl->parent->left = y;
+ else
+ rbl->parent->right = y;
+ }
+
+ /* Linked list delete */
+
+ if(rbl->prev)
+ rbl->prev->next = rbl->next;
+ else
+ rbl->tree->head = rbl->next;
+
+ if(rbl->next)
+ rbl->next->prev = rbl->prev;
+ else
+ rbl->tree->tail = rbl->prev;
+
+ /* Red-black part of delete */
+
+ if(y->color == RBL_BLACK && x)
+ rbl_delete_fixup(x);
+
+ return rbl;
+}
+
+/* Search node in tree and unlink it */
+rbl_t *rbl_unlink(rbltree_t *tree, void *data)
+{
+ rbl_t *rbl;
+
+ rbl = rbl_search_rbl(tree, data);
+
+ if(rbl)
+ rbl_unlink_rbl(rbl);
+
+ return rbl;
+}
+
+/* Unlink node and free it */
+void rbl_delete_rbl(rbl_t *rbl)
+{
+ rbl_unlink_rbl(rbl);
+ free_rbl(rbl);
+}
+
+/* Search node in tree, unlink and free it */
+void rbl_delete(rbltree_t *tree, void *data)
+{
+ rbl_t *rbl;
+
+ rbl = rbl_unlink(tree, data);
+
+ if(rbl)
+ free_rbl(rbl);
+}
+
+/* Optimized unlinking for a complete tree */
+void rbl_unlink_rbltree(rbltree_t *tree)
+{
+ rbl_t *rbl, *next;
+
+ for(rbl = tree->head; rbl; rbl = next)
+ {
+ next = rbl->next;
+ rbl->tree = NULL;
+ rbl->parent = NULL;
+ rbl->left = NULL;
+ rbl->right = NULL;
+ rbl->prev = NULL;
+ rbl->next = NULL;
+ }
+
+ tree->top = NULL;
+ tree->head = NULL;
+ tree->tail = NULL;
+}
+
+/* Optimized deletion for a complete tree */
+void rbl_delete_rbltree(rbltree_t *tree)
+{
+ rbl_t *rbl, *next;
+
+ for(rbl = tree->head; rbl; rbl = next)
+ {
+ next = rbl->next;
+ free_rbl(rbl);
+ }
+
+ tree->top = NULL;
+ tree->head = NULL;
+ tree->tail = NULL;
+}
+
+/* Do action for each list entry (in order)
+ Deletion of entry for which action is called is allowed.
+ */
+void rbl_foreach(rbltree_t *tree, rbl_action_t action)
+{
+ rbl_t *rbl, *next;
+
+ for(rbl = tree->head; rbl; rbl = next)
+ {
+ next = rbl->next;
+ action(rbl->data);
+ }
+}
+
+void rbl_foreach_rbl(rbltree_t *tree, rbl_action_rbl_t action)
+{
+ rbl_t *rbl, *next;
+
+ for(rbl = tree->head; rbl; rbl = next)
+ {
+ next = rbl->next;
+ action(rbl);
+ }
+}
--- /dev/null
+/*
+ rbl.h -- header file for rbl.c
+ Copyright (C) 2000 Ivo Timmermans <itimmermans@bigfoot.com>,
+ 2000 Guus Sliepen <guus@sliepen.warande.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: rbl.h,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+#ifndef __RBL_H__
+#define __RBL_H__
+
+#define RBL_FOREACH(tree,rbl) for(rbl = tree->head; rbl; rbl = rbl->next)
+
+typedef struct rbl_t
+{
+ /* 'red-black tree' part */
+
+ struct rbltree_t *tree;
+
+ int color;
+
+ struct rbl_t *parent;
+ struct rbl_t *left;
+ struct rbl_t *right;
+
+ /* 'linked list' part */
+
+ struct rbl_t *prev;
+ struct rbl_t *next;
+
+ /* payload */
+
+ void *data;
+
+} rbl_t;
+
+typedef int (*rbl_compare_t) (const void *, const void *);
+typedef void (*rbl_action_t) (const void *);
+typedef void (*rbl_action_rbl_t) (const struct rbl_t *);
+
+typedef struct rbltree_t
+{
+ /* callback functions */
+
+ rbl_compare_t compare;
+ rbl_action_t delete;
+
+ /* tree part */
+
+ struct rbl_t *top;
+
+ /* linked list */
+
+ struct rbl_t *head;
+ struct rbl_t *tail;
+
+} rbltree_t;
+
+enum color
+{
+ RBL_RED,
+ RBL_BLACK
+} color;
+
+extern rbltree_t *new_rbltree(rbl_compare_t, rbl_action_t);
+extern void free_rbltree(rbltree_t *);
+extern rbl_t *new_rbl(void);
+extern void free_rbl(rbl_t *);
+
+extern void *rbl_search(rbltree_t *, void *);
+extern void *rbl_search_closest(rbltree_t *, void *);
+extern void *rbl_search_closest_greater(rbltree_t *, void *);
+extern void *rbl_search_closest_smaller(rbltree_t *, void *);
+extern rbl_t *rbl_search_rbl(rbltree_t *, void *);
+extern rbl_t *rbl_search_closest_rbl(rbltree_t *, void *);
+extern rbl_t *rbl_search_closest_greater_rbl(rbltree_t *, void *);
+extern rbl_t *rbl_search_closest_smaller_rbl(rbltree_t *, void *);
+extern rbl_t *rbl_insert(rbltree_t *, void *);
+extern rbl_t *rbl_unlink(rbltree_t *, void *);
+extern void rbl_delete(rbltree_t *, void *);
+extern rbl_t *rbl_insert_rbl(rbltree_t *, rbl_t *);
+extern rbl_t *rbl_unlink_rbl(rbl_t *);
+extern void rbl_delete_rbl(rbl_t *);
+extern void rbl_unlink_rbltree(rbltree_t *);
+extern void rbl_delete_rbltree(rbltree_t *);
+
+extern void rbl_foreach(rbltree_t *, rbl_action_t);
+extern void rbl_foreach_rbl(rbltree_t *, rbl_action_rbl_t);
+
+#endif /* __RBL_H__ */
/*
utils.c -- gathering of some stupid small functions
- Copyright (C) 1999,2000 Ivo Timmermans <zarq@iname.com>
- 2000 Guus Sliepen <guus@sliepen.warande.net>
+ Copyright (C) 1999-2001 Ivo Timmermans <zarq@iname.com>
+ 2000,2001 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <utils.h>
#include <syslog.h>
+#include <xalloc.h>
-volatile int (cp_line[]) = {0, 0, 0, 0, 0, 0, 0, 0};
-volatile char (*cp_file[]) = {"?", "?", "?", "?", "?", "?", "?", "?"};
+#ifdef ENABLE_TRACING
+volatile int (cp_line[]) = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+volatile char (*cp_file[]) = {"?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?"};
volatile int cp_index = 0;
+#endif
char *hexadecimals = "0123456789ABCDEF";
}
}
-char *cp_trace()
+#ifdef ENABLE_TRACING
+void cp_trace()
{
- syslog(LOG_DEBUG, "Checkpoint trace: %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d ...",
- cp_file[(cp_index+7)%8], cp_line[(cp_index+7)%8],
- cp_file[(cp_index+6)%8], cp_line[(cp_index+6)%8],
- cp_file[(cp_index+5)%8], cp_line[(cp_index+5)%8],
- cp_file[(cp_index+4)%8], cp_line[(cp_index+4)%8],
- cp_file[(cp_index+3)%8], cp_line[(cp_index+3)%8],
- cp_file[(cp_index+2)%8], cp_line[(cp_index+2)%8],
- cp_file[(cp_index+1)%8], cp_line[(cp_index+1)%8],
+ syslog(LOG_DEBUG, "Checkpoint trace: %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d <- %s:%d...",
+ cp_file[(cp_index+15)%16], cp_line[(cp_index+15)%16],
+ cp_file[(cp_index+14)%16], cp_line[(cp_index+14)%16],
+ cp_file[(cp_index+13)%16], cp_line[(cp_index+13)%16],
+ cp_file[(cp_index+12)%16], cp_line[(cp_index+12)%16],
+ cp_file[(cp_index+11)%16], cp_line[(cp_index+11)%16],
+ cp_file[(cp_index+10)%16], cp_line[(cp_index+10)%16],
+ cp_file[(cp_index+9)%16], cp_line[(cp_index+9)%16],
+ cp_file[(cp_index+8)%16], cp_line[(cp_index+8)%16],
+ cp_file[(cp_index+7)%16], cp_line[(cp_index+7)%16],
+ cp_file[(cp_index+6)%16], cp_line[(cp_index+6)%16],
+ cp_file[(cp_index+5)%16], cp_line[(cp_index+5)%16],
+ cp_file[(cp_index+4)%16], cp_line[(cp_index+4)%16],
+ cp_file[(cp_index+3)%16], cp_line[(cp_index+3)%16],
+ cp_file[(cp_index+2)%16], cp_line[(cp_index+2)%16],
+ cp_file[(cp_index+1)%16], cp_line[(cp_index+1)%16],
cp_file[cp_index], cp_line[cp_index]
);
}
+#endif
/*
utils.h -- header file for utils.c
- Copyright (C) 1999,2000 Ivo Timmermans <zarq@iname.com>
- 2000 Guus Sliepen <guus@sliepen.warande.net>
+ Copyright (C) 1999-2001 Ivo Timmermans <zarq@iname.com>
+ 2000,2001 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <ctype.h>
enum {
- DEBUG_CONNECTIONS = 0,
- DEBUG_PROTOCOL,
- DEBUG_STATUS,
- DEBUG_ERROR,
- DEBUG_META
+ DEBUG_NOTHING = 0, /* Quiet mode, only show starting/stopping of the daemon */
+ DEBUG_CONNECTIONS = 1, /* Show (dis)connects of other tinc daemons via TCP */
+ DEBUG_ERROR = 2, /* Show error messages received from other hosts */
+ DEBUG_STATUS = 2, /* Show status messages received from other hosts */
+ DEBUG_PROTOCOL = 3, /* Show the requests that are sent/received */
+ DEBUG_META = 4, /* Show contents of every request that is sent/received */
+ DEBUG_TRAFFIC = 5, /* Show network traffic information */
+ DEBUG_PACKET = 6, /* Show contents of each packet that is being sent/received */
+ DEBUG_SCARY_THINGS = 10 /* You have been warned */
};
#define min(a,b) (((a)<(b))?(a):(b))
+#ifdef ENABLE_TRACING
extern volatile int cp_line[];
extern volatile char *cp_file[];
extern volatile int cp_index;
+extern void cp_trace(void);
-#define cp { cp_line[cp_index] = __LINE__; cp_file[cp_index] = __FILE__; cp_index++; cp_index %= 8; }
-#define ecp { fprintf(stderr, "Explicit checkpoint in %s line %d\n", __FILE__, __LINE__); }
+ #define cp { cp_line[cp_index] = __LINE__; cp_file[cp_index] = __FILE__; cp_index++; cp_index %= 16; }
+ #define ecp { fprintf(stderr, "Explicit checkpoint in %s line %d\n", __FILE__, __LINE__); }
+#else
+ #define cp
+ #define ecp
+ #define cp_trace()
+#endif
extern void hex2bin(char *src, char *dst, int length);
extern void bin2hex(char *src, char *dst, int length);
-extern char *cp_trace(void);
#endif /* __TINC_UTILS_H__ */
void *xmalloc_and_zero PARAMS ((size_t n));
void *xcalloc PARAMS ((size_t n, size_t s));
void *xrealloc PARAMS ((void *p, size_t n));
+
+char *xstrdup PARAMS ((const char *s));
#include <sys/types.h>
#include <stdio.h>
+#include <string.h>
#if STDC_HEADERS
# include <stdlib.h>
return p;
}
+/* Duplicate a string */
+
+char *xstrdup(const char *s)
+{
+ char *p;
+
+ p = strdup(s);
+ if(!p)
+ xalloc_fail ((int)strlen(s));
+ return p;
+}
+
#ifdef NOT_USED
/* Allocate memory for N elements of S bytes, with error checking. */
/*
conf.c -- configuration code
- Copyright (C) 1998 Emphyrio,
- Copyright (C) 1998,1999,2000 Ivo Timmermans <itimmermans@bigfoot.com>
- 2000 Guus Sliepen <guus@sliepen.warande.net>
- 2000 Cris van Pelt <tribbel@arise.dhs.org>
+ Copyright (C) 1998 Robert van der Meulen
+ 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>
+ 2000-2002 Guus Sliepen <guus@sliepen.warande.net>
+ 2000 Cris van Pelt <tribbel@arise.dhs.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- $Id: conf.c,v 1.10 2000/10/18 20:12:08 zarq Exp $
+ $Id: conf.c,v 1.11 2002/04/09 15:26:00 zarq Exp $
*/
+#include "config.h"
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <string.h>
#include <xalloc.h>
+#include <utils.h> /* for cp */
+#include <avl_tree.h>
#include "conf.h"
-#include "netutl.h" /* for strtoip */
-#include <utils.h> /* for cp */
+#include "netutl.h" /* for str2address */
-#include "config.h"
-#include "connlist.h"
#include "system.h"
-config_t *config = NULL;
+avl_tree_t *config_tree;
+
int debug_lvl = 0;
-int timeout = 0; /* seconds before timeout */
+int pingtimeout = 0; /* seconds before timeout */
char *confbase = NULL; /* directory in which all config files are */
char *netname = NULL; /* name of the vpn network */
-/* Will be set if HUP signal is received. It will be processed when it is safe. */
-int sighup = 0;
+int config_compare(config_t *a, config_t *b)
+{
+ int result;
-/*
- These are all the possible configurable values
-*/
-static internal_config_t hazahaza[] = {
-/* Main configuration file keywords */
- { "Name", tincname, TYPE_NAME },
- { "ConnectTo", connectto, TYPE_NAME },
- { "PingTimeout", pingtimeout, TYPE_INT },
- { "TapDevice", tapdevice, TYPE_NAME },
- { "TapSubnet", tapsubnet, TYPE_IP },
- { "PrivateKey", privatekey, TYPE_NAME },
- { "KeyExpire", keyexpire, TYPE_INT },
- { "Hostnames", resolve_dns, TYPE_BOOL },
- { "Interface", interface, TYPE_NAME },
- { "InterfaceIP", interfaceip, TYPE_IP },
-/* Host configuration file keywords */
- { "Address", address, TYPE_NAME },
- { "Port", port, TYPE_INT },
- { "PublicKey", publickey, TYPE_NAME },
- { "Subnet", subnet, TYPE_NAME },
- { "RestrictHosts", restricthosts, TYPE_BOOL },
- { "RestrictSubnets", restrictsubnets, TYPE_BOOL },
- { "RestrictAddress", restrictaddress, TYPE_BOOL },
- { "RestrictPort", restrictport, TYPE_BOOL },
- { "IndirectData", indirectdata, TYPE_BOOL },
- { "TCPonly", tcponly, TYPE_BOOL },
- { NULL, 0, 0 }
-};
+ result = strcasecmp(a->variable, b->variable);
-/*
- Add given value to the list of configs cfg
-*/
-config_t *
-add_config_val(config_t **cfg, int argtype, char *val)
+ if(result)
+ return result;
+
+ result = a->line - b->line;
+
+ if(result)
+ return result;
+ else
+ return strcmp(a->file, b->file);
+}
+
+void init_configuration(avl_tree_t **config_tree)
+{
+cp
+ *config_tree = avl_alloc_tree((avl_compare_t)config_compare, (avl_action_t)free_config);
+cp
+}
+
+void exit_configuration(avl_tree_t **config_tree)
+{
+cp
+ avl_delete_tree(*config_tree);
+ *config_tree = NULL;
+cp
+}
+
+config_t *new_config(void)
+{
+ config_t *cfg;
+cp
+ cfg = (config_t *)xmalloc_and_zero(sizeof(*cfg));
+
+ return cfg;
+}
+
+void free_config(config_t *cfg)
{
- config_t *p, *r;
- char *q;
cp
- p = (config_t*)xmalloc(sizeof(*p));
- p->data.val = 0;
+ if(cfg->variable)
+ free(cfg->variable);
+ if(cfg->value)
+ free(cfg->value);
+ if(cfg->file)
+ free(cfg->file);
+ free(cfg);
+cp
+}
+
+void config_add(avl_tree_t *config_tree, config_t *cfg)
+{
+cp
+ avl_insert(config_tree, cfg);
+cp
+}
+
+config_t *lookup_config(avl_tree_t *config_tree, char *variable)
+{
+ config_t cfg, *found;
+cp
+ cfg.variable = variable;
+ cfg.file = "";
+ cfg.line = 0;
+
+ found = avl_search_closest_greater(config_tree, &cfg);
- switch(argtype)
+ if(!found)
+ return NULL;
+
+ if(strcasecmp(found->variable, variable))
+ return NULL;
+
+ return found;
+}
+
+config_t *lookup_config_next(avl_tree_t *config_tree, config_t *cfg)
+{
+ avl_node_t *node;
+ config_t *found;
+cp
+ node = avl_search_node(config_tree, cfg);
+
+ if(node)
{
- case TYPE_INT:
- p->data.val = strtol(val, &q, 0);
- if(q && *q)
- p->data.val = 0;
- break;
- case TYPE_NAME:
- p->data.ptr = xmalloc(strlen(val) + 1);
- strcpy(p->data.ptr, val);
- break;
- case TYPE_IP:
- p->data.ip = strtoip(val);
- break;
- case TYPE_BOOL:
- if(!strcasecmp("yes", val))
- p->data.val = stupid_true;
- else if(!strcasecmp("no", val))
- p->data.val = stupid_false;
- else
- p->data.val = 0;
+ if(node->next)
+ {
+ found = (config_t *)node->next->data;
+ if(!strcasecmp(found->variable, cfg->variable))
+ return found;
+ }
+ }
+
+ return NULL;
+}
+
+int get_config_bool(config_t *cfg, int *result)
+{
+cp
+ if(!cfg)
+ return 0;
+
+ if(!strcasecmp(cfg->value, "yes"))
+ {
+ *result = 1;
+ return 1;
}
+ else if(!strcasecmp(cfg->value, "no"))
+ {
+ *result = 0;
+ return 1;
+ }
+
+ syslog(LOG_ERR, _("\"yes\" or \"no\" expected for configuration variable %s in %s line %d"),
+ cfg->variable, cfg->file, cfg->line);
+
+ return 0;
+}
+
+int get_config_int(config_t *cfg, int *result)
+{
+cp
+ if(!cfg)
+ return 0;
+
+ if(sscanf(cfg->value, "%d", result) == 1)
+ return 1;
+
+ syslog(LOG_ERR, _("Integer expected for configuration variable %s in %s line %d"),
+ cfg->variable, cfg->file, cfg->line);
+ return 0;
+}
+
+int get_config_string(config_t *cfg, char **result)
+{
+cp
+ if(!cfg)
+ return 0;
+
+ *result = xstrdup(cfg->value);
+ return 1;
+}
+
+int get_config_address(config_t *cfg, struct addrinfo **result)
+{
+ struct addrinfo *ai;
+cp
+ if(!cfg)
+ return 0;
+
+ ai = str2addrinfo(cfg->value, NULL, 0);
+
+ if(ai)
+ {
+ *result = ai;
+ return 1;
+ }
+
+ syslog(LOG_ERR, _("Hostname or IP address expected for configuration variable %s in %s line %d"),
+ cfg->variable, cfg->file, cfg->line);
+ return 0;
+}
- p->argtype = argtype;
+int get_config_port(config_t *cfg, port_t *result)
+{
+cp
+ if(!cfg)
+ return 0;
- if(p->data.val)
+ if(sscanf(cfg->value, "%hu", result) == 1)
{
- p->next = *cfg;
- *cfg = p;
+ *result = htons(*result);
+ return 1;
+ }
+
+ syslog(LOG_ERR, _("Port number expected for configuration variable %s in %s line %d"),
+ cfg->variable, cfg->file, cfg->line);
+ return 0;
+}
+
+int get_config_subnet(config_t *cfg, subnet_t **result)
+{
+ subnet_t *subnet;
cp
- return p;
+ if(!cfg)
+ return 0;
+
+ subnet = str2net(cfg->value);
+
+ if(!subnet)
+ {
+ syslog(LOG_ERR, _("Subnet expected for configuration variable %s in %s line %d"),
+ cfg->variable, cfg->file, cfg->line);
+ return 0;
+ }
+
+ /* Teach newbies what subnets are... */
+
+ if(((subnet->type == SUBNET_IPV4) && maskcheck((char *)&subnet->net.ipv4.address, subnet->net.ipv4.prefixlength, sizeof(ipv4_t)))
+ || ((subnet->type == SUBNET_IPV6) && maskcheck((char *)&subnet->net.ipv6.address, subnet->net.ipv6.prefixlength, sizeof(ipv6_t))))
+ {
+ syslog(LOG_ERR, _("Network address and prefix length do not match for configuration variable %s in %s line %d"),
+ cfg->variable, cfg->file, cfg->line);
+ free(subnet);
+ return 0;
+ }
+
+ *result = subnet;
+
+ return 1;
+}
+
+/*
+ Read exactly one line and strip the trailing newline if any. If the
+ file was on EOF, return NULL. Otherwise, return all the data in a
+ dynamically allocated buffer.
+
+ If line is non-NULL, it will be used as an initial buffer, to avoid
+ unnecessary mallocing each time this function is called. If buf is
+ given, and buf needs to be expanded, the var pointed to by buflen
+ will be increased.
+*/
+char *readline(FILE *fp, char **buf, size_t *buflen)
+{
+ char *newline = NULL;
+ char *p;
+ char *line; /* The array that contains everything that has been read
+ so far */
+ char *idx; /* Read into this pointer, which points to an offset
+ within line */
+ size_t size, newsize; /* The size of the current array pointed to by
+ line */
+ size_t maxlen; /* Maximum number of characters that may be read with
+ fgets. This is newsize - oldsize. */
+
+ if(feof(fp))
+ return NULL;
+
+ if((buf != NULL) && (buflen != NULL))
+ {
+ size = *buflen;
+ line = *buf;
}
else
{
- free(p);
-cp
- return NULL;
+ size = 100;
+ line = xmalloc(size);
+ }
+
+ maxlen = size;
+ idx = line;
+ *idx = 0;
+ for(;;)
+ {
+ errno = 0;
+ p = fgets(idx, maxlen, fp);
+ if(p == NULL) /* EOF or error */
+ {
+ if(feof(fp))
+ break;
+
+ /* otherwise: error; let the calling function print an error
+ message if applicable */
+ free(line);
+ return NULL;
+ }
+
+ newline = strchr(p, '\n');
+ if(newline == NULL)
+ /* We haven't yet read everything to the end of the line */
+ {
+ newsize = size << 1;
+ line = xrealloc(line, newsize);
+ idx = &line[size - 1];
+ maxlen = newsize - size + 1;
+ size = newsize;
+ }
+ else
+ {
+ *newline = '\0'; /* kill newline */
+ break; /* yay */
+ }
+ }
+
+ if((buf != NULL) && (buflen != NULL))
+ {
+ *buflen = size;
+ *buf = line;
}
+ return line;
}
/*
Parse a configuration file and put the results in the configuration tree
starting at *base.
*/
-int read_config_file(config_t **base, const char *fname)
+int read_config_file(avl_tree_t *config_tree, const char *fname)
{
- int err = -1;
+ int err = -2; /* Parse error */
FILE *fp;
- char line[MAXBUFSIZE]; /* There really should not be any line longer than this... */
- char *p, *q;
- int i, lineno = 0;
+ char *buffer, *line;
+ char *variable, *value;
+ int lineno = 0, ignore = 0;
config_t *cfg;
+ size_t bufsize;
+
cp
if((fp = fopen (fname, "r")) == NULL)
{
- return -1;
+ syslog(LOG_ERR, _("Cannot open config file %s: %s"), fname, strerror(errno));
+ return -3;
}
+ bufsize = 100;
+ buffer = xmalloc(bufsize);
+
for(;;)
{
- if(fgets(line, MAXBUFSIZE, fp) == NULL)
- {
- err = 0;
- break;
- }
-
- lineno++;
+ if((line = readline(fp, &buffer, &bufsize)) == NULL)
+ {
+ err = -1;
+ break;
+ }
- if(!index(line, '\n'))
- {
- syslog(LOG_ERR, _("Line %d too long while reading config file %s"), lineno, fname);
- break;
- }
+ if(feof(fp))
+ {
+ err = 0;
+ break;
+ }
+
+ lineno++;
- if((p = strtok(line, "\t\n\r =")) == NULL)
+ if((variable = strtok(line, "\t =")) == NULL)
continue; /* no tokens on this line */
- if(p[0] == '#')
+ if(variable[0] == '#')
continue; /* comment: ignore */
- for(i = 0; hazahaza[i].name != NULL; i++)
- if(!strcasecmp(hazahaza[i].name, p))
- break;
+ if(!strcmp(variable, "-----BEGIN"))
+ ignore = 1;
- if(!hazahaza[i].name)
- {
- syslog(LOG_ERR, _("Invalid variable name on line %d while reading config file %s"),
- lineno, fname);
- break;
- }
+ if(!ignore)
+ {
+ if(((value = strtok(NULL, "\t\n\r =")) == NULL) || value[0] == '#')
+ {
+ syslog(LOG_ERR, _("No value for variable `%s' on line %d while reading config file %s"),
+ variable, lineno, fname);
+ break;
+ }
- if(((q = strtok(NULL, "\t\n\r =")) == NULL) || q[0] == '#')
- {
- fprintf(stderr, _("No value for variable on line %d while reading config file %s"),
- lineno, fname);
- break;
- }
+ cfg = new_config();
+ cfg->variable = xstrdup(variable);
+ cfg->value = xstrdup(value);
+ cfg->file = xstrdup(fname);
+ cfg->line = lineno;
- cfg = add_config_val(base, hazahaza[i].argtype, q);
- if(cfg == NULL)
- {
- fprintf(stderr, _("Invalid value for variable on line %d while reading config file %s"),
- lineno, fname);
- break;
- }
+ config_add(config_tree, cfg);
+ }
- cfg->which = hazahaza[i].which;
- if(!config)
- config = cfg;
+ if(!strcmp(variable, "-----END"))
+ ignore = 0;
}
+ free(buffer);
fclose (fp);
cp
return err;
int x;
cp
asprintf(&fname, "%s/tinc.conf", confbase);
- x = read_config_file(&config, fname);
+ x = read_config_file(config_tree, fname);
+ if(x == -1) /* System error: complain */
+ {
+ syslog(LOG_ERR, _("Failed to read `%s': %s"), fname, strerror(errno));
+ }
free(fname);
cp
- return x;
+ return x;
}
-/*
- Look up the value of the config option type
-*/
-const config_t *get_config_val(config_t *p, which_t type)
+int isadir(const char* f)
{
-cp
- for(; p != NULL; p = p->next)
- if(p->which == type)
- break;
-cp
- return p;
-}
+ struct stat s;
-/*
- Support for multiple config lines.
- Index is used to get a specific value, 0 being the first, 1 the second etc.
-*/
-const config_t *get_next_config_val(config_t *p, which_t type, int index)
-{
-cp
- for(; p != NULL; p = p->next)
- if(p->which == type)
- if(--index < 0)
- break;
-cp
- return p;
+ if(stat(f, &s) < 0)
+ return 0;
+ else
+ return S_ISDIR(s.st_mode);
}
-/*
- Remove the complete configuration tree.
-*/
-void clear_config(config_t **base)
+int is_safe_path(const char *file)
{
- config_t *p, *next;
-cp
- for(p = *base; p != NULL; p = next)
+ char *p;
+ const char *f;
+ char x;
+ struct stat s;
+ char l[MAXBUFSIZE];
+
+ if(*file != '/')
+ {
+ syslog(LOG_ERR, _("`%s' is not an absolute path"), file);
+ return 0;
+ }
+
+ p = strrchr(file, '/');
+
+ if(p == file) /* It's in the root */
+ p++;
+
+ x = *p;
+ *p = '\0';
+
+ f = file;
+check1:
+ if(lstat(f, &s) < 0)
+ {
+ syslog(LOG_ERR, _("Couldn't stat `%s': %s"), f, strerror(errno));
+ return 0;
+ }
+
+ if(s.st_uid != geteuid())
+ {
+ syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
+ f, s.st_uid, geteuid());
+ return 0;
+ }
+
+ if(S_ISLNK(s.st_mode))
{
- next = p->next;
- if(p->data.ptr && (p->argtype == TYPE_NAME))
+ syslog(LOG_WARNING, _("Warning: `%s' is a symlink"),
+ f);
+
+ if(readlink(f, l, MAXBUFSIZE) < 0)
{
- free(p->data.ptr);
+ syslog(LOG_ERR, _("Unable to read symbolic link `%s': %s"), f, strerror(errno));
+ return 0;
}
- free(p);
+
+ f = l;
+ goto check1;
}
- *base = NULL;
-cp
+
+ *p = x;
+ f = file;
+
+check2:
+ if(lstat(f, &s) < 0 && errno != ENOENT)
+ {
+ syslog(LOG_ERR, _("Couldn't stat `%s': %s"), f, strerror(errno));
+ return 0;
+ }
+
+ if(errno == ENOENT)
+ return 1;
+
+ if(s.st_uid != geteuid())
+ {
+ syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
+ f, s.st_uid, geteuid());
+ return 0;
+ }
+
+ if(S_ISLNK(s.st_mode))
+ {
+ syslog(LOG_WARNING, _("Warning: `%s' is a symlink"),
+ f);
+
+ if(readlink(f, l, MAXBUFSIZE) < 0)
+ {
+ syslog(LOG_ERR, _("Unable to read symbolic link `%s': %s"), f, strerror(errno));
+ return 0;
+ }
+
+ f = l;
+ goto check2;
+ }
+
+ if(s.st_mode & 0007)
+ {
+ /* Accessible by others */
+ syslog(LOG_ERR, _("`%s' has unsecure permissions"),
+ f);
+ return 0;
+ }
+
+ return 1;
+}
+
+FILE *ask_and_safe_open(const char* filename, const char* what, const char* mode)
+{
+ FILE *r;
+ char *directory;
+ char *fn;
+
+ /* Check stdin and stdout */
+ if(!isatty(0) || !isatty(1))
+ {
+ /* Argh, they are running us from a script or something. Write
+ the files to the current directory and let them burn in hell
+ for ever. */
+ fn = xstrdup(filename);
+ }
+ else
+ {
+ /* Ask for a file and/or directory name. */
+ fprintf(stdout, _("Please enter a file to save %s to [%s]: "),
+ what, filename);
+ fflush(stdout);
+
+ if((fn = readline(stdin, NULL, NULL)) == NULL)
+ {
+ fprintf(stderr, _("Error while reading stdin: %s\n"), strerror(errno));
+ return NULL;
+ }
+
+ if(strlen(fn) == 0)
+ /* User just pressed enter. */
+ fn = xstrdup(filename);
+ }
+
+ if((strchr(fn, '/') == NULL) || (fn[0] != '/'))
+ {
+ /* The directory is a relative path or a filename. */
+ char *p;
+
+ directory = get_current_dir_name();
+ asprintf(&p, "%s/%s", directory, fn);
+ free(fn);
+ free(directory);
+ fn = p;
+ }
+
+ umask(0077); /* Disallow everything for group and other */
+
+ /* Open it first to keep the inode busy */
+ if((r = fopen(fn, mode)) == NULL)
+ {
+ fprintf(stderr, _("Error opening file `%s': %s\n"),
+ fn, strerror(errno));
+ free(fn);
+ return NULL;
+ }
+
+ /* Then check the file for nasty attacks */
+ if(!is_safe_path(fn)) /* Do not permit any directories that are
+ readable or writeable by other users. */
+ {
+ fprintf(stderr, _("The file `%s' (or any of the leading directories) has unsafe permissions.\n"
+ "I will not create or overwrite this file.\n"),
+ fn);
+ fclose(r);
+ free(fn);
+ return NULL;
+ }
+
+ free(fn);
+
+ return r;
}
/*
conf.h -- header for conf.c
- Copyright (C) 1998,1999,2000 Ivo Timmermans <itimmermans@bigfoot.com>
- 2000 Guus Sliepen <guus@sliepen.warande.net>
+ Copyright (C) 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>
+ 2000-2002 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- $Id: conf.h,v 1.7 2000/10/18 20:12:08 zarq Exp $
+ $Id: conf.h,v 1.8 2002/04/09 15:26:00 zarq Exp $
*/
#ifndef __TINC_CONF_H__
#define __TINC_CONF_H__
-#define MAXTIMEOUT 900 /* Maximum timeout value for retries. Should this be a configuration option? */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
-typedef struct ip_mask_t {
- unsigned long ip;
- unsigned long mask;
-} ip_mask_t;
-
-typedef enum which_t {
- tincname = 1,
- connectto,
- pingtimeout,
- tapdevice,
- tapsubnet,
- privatekey,
- keyexpire,
- resolve_dns,
- interface,
- interfaceip,
- address,
- port,
- publickey,
- subnet,
- restricthosts,
- restrictsubnets,
- restrictaddress,
- restrictport,
- indirectdata,
- tcponly,
-} which_t;
+#include <avl_tree.h>
+#include "net.h"
+#include "subnet.h"
typedef struct config_t {
- struct config_t *next;
- which_t which;
- int argtype;
- union data {
- unsigned long val;
- void *ptr;
- ip_mask_t *ip;
- struct config_t *next; /* For nested configs! */
- } data;
+ char *variable;
+ char *value;
+ char *file;
+ int line;
} config_t;
-typedef struct internal_config_t {
- char *name;
- enum which_t which;
- int argtype;
-} internal_config_t;
-
-enum {
- stupid_false = 1,
- stupid_true
-};
+extern avl_tree_t *config_tree;
-enum {
- TYPE_NAME = 1,
- TYPE_INT,
- TYPE_IP,
- TYPE_BOOL
-};
-
-extern config_t *config;
extern int debug_lvl;
-extern int timeout;
-extern int upstreamindex;
-extern int sighup;
+extern int pingtimeout;
+extern int maxtimeout;
+extern int bypass_security;
extern char *confbase;
extern char *netname;
-extern config_t *add_config_val(config_t **, int, char *);
-extern int read_config_file(config_t **, const char *);
-extern const config_t *get_config_val(config_t *, which_t type);
-extern const config_t *get_next_config_val(config_t *, which_t type, int);
-extern void clear_config();
+extern void init_configuration(avl_tree_t **);
+extern void exit_configuration(avl_tree_t **);
+extern config_t *new_config(void);
+extern void free_config(config_t *);
+extern void config_add(avl_tree_t *, config_t *);
+extern config_t *lookup_config(avl_tree_t *, char *);
+extern config_t *lookup_config_next(avl_tree_t *, config_t *);
+extern int get_config_bool(config_t *, int *);
+extern int get_config_int(config_t *, int *);
+extern int get_config_port(config_t *, port_t *);
+extern int get_config_string(config_t *, char **);
+extern int get_config_address(config_t *, struct addrinfo **);
+struct subnet_t; /* Needed for next line. */
+extern int get_config_subnet(config_t *, struct subnet_t **);
+
+extern int read_config_file(avl_tree_t *, const char *);
extern int read_server_config(void);
+extern FILE *ask_and_safe_open(const char*, const char*, const char *);
+extern int is_safe_path(const char *);
#endif /* __TINC_CONF_H__ */
--- /dev/null
+/*
+ connection.c -- connection list management
+ Copyright (C) 2000-2002 Guus Sliepen <guus@sliepen.warande.net>,
+ 2000-2002 Ivo Timmermans <itimmermans@bigfoot.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: connection.c,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <avl_tree.h>
+#include <list.h>
+
+#include "net.h" /* Don't ask. */
+#include "netutl.h"
+#include "config.h"
+#include "conf.h"
+#include <utils.h>
+#include "subnet.h"
+
+#include "xalloc.h"
+#include "system.h"
+
+avl_tree_t *connection_tree; /* Meta connections */
+
+int connection_compare(connection_t *a, connection_t *b)
+{
+ return a - b;
+}
+
+void init_connections(void)
+{
+cp
+ connection_tree = avl_alloc_tree((avl_compare_t)connection_compare, NULL);
+cp
+}
+
+void exit_connections(void)
+{
+cp
+ avl_delete_tree(connection_tree);
+cp
+}
+
+connection_t *new_connection(void)
+{
+ connection_t *c;
+cp
+ c = (connection_t *)xmalloc_and_zero(sizeof(connection_t));
+
+ if(!c)
+ return NULL;
+
+ gettimeofday(&c->start, NULL);
+cp
+ return c;
+}
+
+void free_connection(connection_t *c)
+{
+cp
+ if(c->hostname)
+ free(c->hostname);
+ if(c->inkey)
+ free(c->inkey);
+ if(c->outkey)
+ free(c->outkey);
+ if(c->mychallenge)
+ free(c->mychallenge);
+ if(c->hischallenge)
+ free(c->hischallenge);
+ free(c);
+cp
+}
+
+void connection_add(connection_t *c)
+{
+cp
+ avl_insert(connection_tree, c);
+cp
+}
+
+void connection_del(connection_t *c)
+{
+cp
+ avl_delete(connection_tree, c);
+cp
+}
+
+void dump_connections(void)
+{
+ avl_node_t *node;
+ connection_t *c;
+cp
+ syslog(LOG_DEBUG, _("Connections:"));
+
+ for(node = connection_tree->head; node; node = node->next)
+ {
+ c = (connection_t *)node->data;
+ syslog(LOG_DEBUG, _(" %s at %s options %lx socket %d status %04x"),
+ c->name, c->hostname, c->options, c->socket, c->status);
+ }
+
+ syslog(LOG_DEBUG, _("End of connections."));
+cp
+}
+
+int read_connection_config(connection_t *c)
+{
+ char *fname;
+ int x;
+cp
+ asprintf(&fname, "%s/hosts/%s", confbase, c->name);
+ x = read_config_file(c->config_tree, fname);
+ free(fname);
+cp
+ return x;
+}
--- /dev/null
+/*
+ connection.h -- header for connection.c
+ Copyright (C) 2000-2002 Guus Sliepen <guus@sliepen.warande.net>,
+ 2000-2002 Ivo Timmermans <itimmermans@bigfoot.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: connection.h,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+#ifndef __TINC_CONNECTION_H__
+#define __TINC_CONNECTION_H__
+
+#include <sys/time.h>
+
+#include <avl_tree.h>
+#include <list.h>
+
+#ifdef HAVE_OPENSSL_EVP_H
+# include <openssl/evp.h>
+#else
+# include <evp.h>
+#endif
+
+#ifdef HAVE_OPENSSL_RSA_H
+# include <openssl/rsa.h>
+#else
+# include <rsa.h>
+#endif
+
+#include "net.h"
+#include "conf.h"
+
+#include "node.h"
+#include "edge.h"
+
+#define OPTION_INDIRECT 0x0001
+#define OPTION_TCPONLY 0x0002
+
+typedef struct connection_status_t {
+ int pinged:1; /* sent ping */
+ int active:1; /* 1 if active.. */
+ int connecting:1; /* 1 if we are waiting for a non-blocking connect() to finish */
+ int termreq:1; /* the termination of this connection was requested */
+ int remove:1; /* Set to 1 if you want this connection removed */
+ int timeout:1; /* 1 if gotten timeout */
+ int encryptout:1; /* 1 if we can encrypt outgoing traffic */
+ int decryptin:1; /* 1 if we have to decrypt incoming traffic */
+ int mst:1; /* 1 if this connection is part of a minimum spanning tree */
+ int unused:18;
+} connection_status_t;
+
+typedef struct connection_t {
+ char *name; /* name he claims to have */
+
+ sockaddr_t address; /* his real (internet) ip */
+ char *hostname; /* the hostname of its real ip */
+ int protocol_version; /* used protocol */
+
+ int socket; /* socket used for this connection */
+ long int options; /* options for this connection */
+ struct connection_status_t status; /* status info */
+ int estimated_weight; /* estimation for the weight of the edge for this connection */
+ struct timeval start; /* time this connection was started, used for above estimation */
+ struct outgoing_t *outgoing; /* used to keep track of outgoing connections */
+
+ struct node_t *node; /* node associated with the other end */
+ struct edge_t *edge; /* edge associated with this connection */
+
+ RSA *rsa_key; /* his public/private key */
+ const EVP_CIPHER *incipher; /* Cipher he will use to send data to us */
+ const EVP_CIPHER *outcipher; /* Cipher we will use to send data to him */
+ EVP_CIPHER_CTX *inctx; /* Context of encrypted meta data that will come from him to us */
+ EVP_CIPHER_CTX *outctx; /* Context of encrypted meta data that will be sent from us to him */
+ char *inkey; /* His symmetric meta key + iv */
+ char *outkey; /* Our symmetric meta key + iv */
+ int inkeylength; /* Length of his key + iv */
+ int outkeylength; /* Length of our key + iv */
+ const EVP_MD *indigest;
+ const EVP_MD *outdigest;
+ int inmaclength;
+ int outmaclength;
+ int incompression;
+ int outcompression;
+ char *mychallenge; /* challenge we received from him */
+ char *hischallenge; /* challenge we sent to him */
+
+ char buffer[MAXBUFSIZE]; /* metadata input buffer */
+ int buflen; /* bytes read into buffer */
+ int tcplen; /* length of incoming TCPpacket */
+ int allow_request; /* defined if there's only one request possible */
+
+ time_t last_ping_time; /* last time we saw some activity from the other end */
+
+ avl_tree_t *config_tree; /* Pointer to configuration tree belonging to him */
+} connection_t;
+
+extern avl_tree_t *connection_tree;
+
+extern void init_connections(void);
+extern void exit_connections(void);
+extern connection_t *new_connection(void);
+extern void free_connection(connection_t *);
+extern void connection_add(connection_t *);
+extern void connection_del(connection_t *);
+extern void dump_connections(void);
+extern int read_connection_config(connection_t *);
+
+#endif /* __TINC_CONNECTION_H__ */
--- /dev/null
+/*
+ net.h -- generic header for device.c
+ Copyright (C) 2001-2002 Ivo Timmermans <zarq@iname.com>
+ 2001-2002 Guus Sliepen <guus@sliepen.warande.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: device.h,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+#ifndef __TINC_DEVICE_H__
+#define __TINC_DEVICE_H__
+
+extern int device_fd;
+extern char *device;
+extern char *interface;
+
+extern int setup_device(void);
+extern void close_device(void);
+extern int read_packet(vpn_packet_t *);
+extern int write_packet(vpn_packet_t *);
+extern void dump_device_stats(void);
+
+#endif /* __TINC_DEVICE_H__ */
--- /dev/null
+/*
+ edge.c -- edge tree management
+ Copyright (C) 2000-2002 Guus Sliepen <guus@sliepen.warande.net>,
+ 2000-2002 Ivo Timmermans <itimmermans@bigfoot.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: edge.c,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+
+#include <avl_tree.h>
+#include <list.h>
+
+#include "net.h" /* Don't ask. */
+#include "netutl.h"
+#include "config.h"
+#include "conf.h"
+#include <utils.h>
+#include "subnet.h"
+
+#include "xalloc.h"
+#include "system.h"
+
+avl_tree_t *edge_tree; /* Tree with all known edges (replaces active_tree) */
+avl_tree_t *edge_weight_tree; /* Tree with all edges, sorted on weight */
+
+int edge_compare(edge_t *a, edge_t *b)
+{
+ int result;
+
+ result = strcmp(a->from.node->name, b->from.node->name);
+
+ if(result)
+ return result;
+ else
+ return strcmp(a->to.node->name, b->to.node->name);
+}
+
+/* Evil edge_compare() from a parallel universe ;)
+
+int edge_compare(edge_t *a, edge_t *b)
+{
+ int result;
+
+ return (result = strcmp(a->from.node->name, b->from.node->name)) || (result = strcmp(a->to.node->name, b->to.node->name)), result;
+}
+
+*/
+
+int edge_name_compare(edge_t *a, edge_t *b)
+{
+ int result;
+ char *name_a1, *name_a2, *name_b1, *name_b2;
+
+ if(strcmp(a->from.node->name, a->to.node->name) < 0)
+ name_a1 = a->from.node->name, name_a2 = a->to.node->name;
+ else
+ name_a1 = a->to.node->name, name_a2 = a->from.node->name;
+
+ if(strcmp(b->from.node->name, b->to.node->name) < 0)
+ name_b1 = b->from.node->name, name_b2 = b->to.node->name;
+ else
+ name_b1 = b->to.node->name, name_b2 = b->from.node->name;
+
+ result = strcmp(name_a1, name_b1);
+
+ if(result)
+ return result;
+ else
+ return strcmp(name_a2, name_b2);
+}
+
+int edge_weight_compare(edge_t *a, edge_t *b)
+{
+ int result;
+
+ result = a->weight - b->weight;
+
+ if(result)
+ return result;
+ else
+ return edge_name_compare(a, b);
+}
+
+void init_edges(void)
+{
+cp
+ edge_tree = avl_alloc_tree((avl_compare_t)edge_compare, NULL);
+ edge_weight_tree = avl_alloc_tree((avl_compare_t)edge_weight_compare, NULL);
+cp
+}
+
+avl_tree_t *new_edge_tree(void)
+{
+cp
+ return avl_alloc_tree((avl_compare_t)edge_name_compare, NULL);
+cp
+}
+
+void free_edge_tree(avl_tree_t *edge_tree)
+{
+cp
+ avl_delete_tree(edge_tree);
+cp
+}
+
+void exit_edges(void)
+{
+cp
+ avl_delete_tree(edge_tree);
+cp
+}
+
+/* Creation and deletion of connection elements */
+
+edge_t *new_edge(void)
+{
+ edge_t *e;
+cp
+ e = (edge_t *)xmalloc_and_zero(sizeof(*e));
+cp
+ return e;
+}
+
+void free_edge(edge_t *e)
+{
+cp
+ free(e);
+cp
+}
+
+void edge_add(edge_t *e)
+{
+cp
+ avl_insert(edge_tree, e);
+ avl_insert(edge_weight_tree, e);
+ avl_insert(e->from.node->edge_tree, e);
+ avl_insert(e->to.node->edge_tree, e);
+cp
+}
+
+void edge_del(edge_t *e)
+{
+cp
+ avl_delete(edge_tree, e);
+ avl_delete(edge_weight_tree, e);
+ avl_delete(e->from.node->edge_tree, e);
+ avl_delete(e->to.node->edge_tree, e);
+cp
+}
+
+edge_t *lookup_edge(node_t *from, node_t *to)
+{
+ edge_t v, *result;
+cp
+ v.from.node = from;
+ v.to.node = to;
+
+ result = avl_search(edge_tree, &v);
+
+ if(result)
+ return result;
+cp
+ v.from.node = to;
+ v.to.node = from;
+
+ return avl_search(edge_tree, &v);
+}
+
+void dump_edges(void)
+{
+ avl_node_t *node;
+ edge_t *e;
+ char *from_udp, *to_udp;
+cp
+ syslog(LOG_DEBUG, _("Edges:"));
+
+ for(node = edge_tree->head; node; node = node->next)
+ {
+ e = (edge_t *)node->data;
+ from_udp = sockaddr2hostname(&e->from.udpaddress);
+ to_udp = sockaddr2hostname(&e->to.udpaddress);
+ syslog(LOG_DEBUG, _(" %s at %s - %s at %s options %lx weight %d"),
+ e->from.node->name, from_udp,
+ e->to.node->name, to_udp,
+ e->options, e->weight);
+ free(from_udp);
+ free(to_udp);
+ }
+
+ syslog(LOG_DEBUG, _("End of edges."));
+cp
+}
--- /dev/null
+/*
+ edge.h -- header for edge.c
+ Copyright (C) 2001-2002 Guus Sliepen <guus@sliepen.warande.net>,
+ 2001-2002 Ivo Timmermans <itimmermans@bigfoot.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: edge.h,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+#ifndef __TINC_EDGE_H__
+#define __TINC_EDGE_H__
+
+#include <avl_tree.h>
+
+#include "net.h"
+#include "node.h"
+#include "connection.h"
+
+typedef struct halfconnection_t {
+ struct node_t *node; /* node associated with this end of the connection */
+// sockaddr_t tcpaddress; /* real (internet) ip on this end of the meta connection */
+ sockaddr_t udpaddress; /* real (internet) ip on this end of the vpn connection */
+} halfconnection_t;
+
+typedef struct edge_t {
+ struct halfconnection_t from;
+ struct halfconnection_t to;
+
+ long int options; /* options turned on for this edge */
+ int weight; /* weight of this edge */
+
+ struct connection_t *connection; /* connection associated with this edge, if available */
+} edge_t;
+
+extern avl_tree_t *edge_tree; /* Tree with all known edges (replaces active_tree) */
+extern avl_tree_t *edge_weight_tree; /* Tree with all known edges sorted on weight */
+
+extern void init_edges(void);
+extern void exit_edges(void);
+extern edge_t *new_edge(void);
+extern void free_edge(edge_t *);
+extern avl_tree_t *new_edge_tree(void);
+extern void free_edge_tree(avl_tree_t *);
+extern void edge_add(edge_t *);
+extern void edge_del(edge_t *);
+extern edge_t *lookup_edge(struct node_t *, struct node_t *);
+extern void dump_edges(void);
+
+#endif /* __TINC_EDGE_H__ */
--- /dev/null
+/*
+ event.c -- event queue
+ Copyright (C) 2002 Guus Sliepen <guus@sliepen.warande.net>,
+ 2002 Ivo Timmermans <itimmermans@bigfoot.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: event.c,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <xalloc.h>
+#include <string.h>
+#include <utils.h>
+#include <avl_tree.h>
+#include <time.h>
+
+#include "event.h"
+
+#include "system.h"
+
+avl_tree_t *event_tree;
+extern time_t now;
+
+int id;
+
+int event_compare(event_t *a, event_t *b)
+{
+ if(a->time > b->time)
+ return 1;
+ if(a->time < b->time)
+ return -1;
+ return a->id - b->id;
+}
+
+void init_events(void)
+{
+cp
+ event_tree = avl_alloc_tree((avl_compare_t)event_compare, NULL);
+cp
+}
+
+void exit_events(void)
+{
+cp
+ avl_delete_tree(event_tree);
+cp
+}
+
+event_t *new_event(void)
+{
+ event_t *event;
+cp
+ event = (event_t *)xmalloc_and_zero(sizeof(*event));
+cp
+ return event;
+}
+
+void free_event(event_t *event)
+{
+cp
+ free(event);
+cp
+}
+
+void event_add(event_t *event)
+{
+cp
+ event->id = ++id;
+ avl_insert(event_tree, event);
+cp
+}
+
+void event_del(event_t *event)
+{
+cp
+ avl_delete(event_tree, event);
+cp
+}
+
+event_t *get_expired_event(void)
+{
+ event_t *event;
+cp
+ if(event_tree->head)
+ {
+ event = (event_t *)event_tree->head->data;
+ if(event->time < now)
+ {
+ avl_delete(event_tree, event);
+ return event;
+ }
+ }
+cp
+ return NULL;
+}
--- /dev/null
+/*
+ event.h -- header for event.c
+ Copyright (C) 2002 Guus Sliepen <guus@sliepen.warande.net>,
+ 2002 Ivo Timmermans <itimmermans@bigfoot.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: event.h,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+#ifndef __TINC_EVENT_H__
+#define __TINC_EVENT_H__
+
+#include <time.h>
+#include <avl_tree.h>
+
+avl_tree_t *event_tree;
+
+typedef void (*event_handler_t)(void *);
+
+typedef struct {
+ time_t time;
+ int id;
+ event_handler_t handler;
+ void *data;
+} event_t;
+
+extern void init_events(void);
+extern void exit_events(void);
+extern event_t *new_event(void);
+extern void free_event(event_t *);
+extern void event_add(event_t *);
+extern void event_del(event_t *);
+extern event_t *get_expired_event(void);
+
+#endif /* __TINC_EVENT_H__ */
--- /dev/null
+/*
+ graph.c -- graph algorithms
+ Copyright (C) 2001-2002 Guus Sliepen <guus@sliepen.warande.net>,
+ 2001-2002 Ivo Timmermans <itimmermans@bigfoot.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: graph.c,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+/* We need to generate two trees from the graph:
+
+ 1. A minimum spanning tree for broadcasts,
+ 2. A single-source shortest path tree for unicasts.
+
+ Actually, the first one alone would suffice but would make unicast packets
+ take longer routes than necessary.
+
+ For the MST algorithm we can choose from Prim's or Kruskal's. I personally
+ favour Kruskal's, because we make an extra AVL tree of edges sorted on
+ weights (metric). That tree only has to be updated when an edge is added or
+ removed, and during the MST algorithm we just have go linearly through that
+ tree, adding safe edges until #edges = #nodes - 1. The implementation here
+ however is not so fast, because I tried to avoid having to make a forest and
+ merge trees.
+
+ For the SSSP algorithm Dijkstra's seems to be a nice choice. Currently a
+ simple breadth-first search is presented here.
+
+ The SSSP algorithm will also be used to determine whether nodes are directly,
+ indirectly or not reachable from the source. It will also set the correct
+ destination address and port of a node if possible.
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <syslog.h>
+#include "config.h"
+#include <string.h>
+#if defined(HAVE_FREEBSD) || defined(HAVE_OPENBSD)
+ #include <sys/param.h>
+#endif
+#include <netinet/in.h>
+
+#include <avl_tree.h>
+#include <utils.h>
+
+#include "netutl.h"
+#include "node.h"
+#include "edge.h"
+#include "connection.h"
+#include "process.h"
+
+#include "system.h"
+
+/* Implementation of Kruskal's algorithm.
+ Running time: O(EN)
+ Please note that sorting on weight is already done by add_edge().
+*/
+
+void mst_kruskal(void)
+{
+ avl_node_t *node, *next;
+ edge_t *e;
+ node_t *n;
+ connection_t *c;
+ int nodes = 0;
+ int safe_edges = 0;
+ int skipped;
+
+ /* Clear MST status on connections */
+
+ for(node = connection_tree->head; node; node = node->next)
+ {
+ c = (connection_t *)node->data;
+ c->status.mst = 0;
+ }
+
+ /* Do we have something to do at all? */
+
+ if(!edge_weight_tree->head)
+ return;
+
+ if(debug_lvl >= DEBUG_SCARY_THINGS)
+ syslog(LOG_DEBUG, "Running Kruskal's algorithm:");
+
+ /* Clear visited status on nodes */
+
+ for(node = node_tree->head; node; node = node->next)
+ {
+ n = (node_t *)node->data;
+ n->status.visited = 0;
+ nodes++;
+ }
+
+ /* Starting point */
+
+ ((edge_t *)edge_weight_tree->head->data)->from.node->status.visited = 1;
+
+ /* Add safe edges */
+
+ for(skipped = 0, node = edge_weight_tree->head; node; node = next)
+ {
+ next = node->next;
+ e = (edge_t *)node->data;
+
+ if(e->from.node->status.visited == e->to.node->status.visited)
+ {
+ skipped = 1;
+ continue;
+ }
+
+ e->from.node->status.visited = 1;
+ e->to.node->status.visited = 1;
+ if(e->connection)
+ e->connection->status.mst = 1;
+
+ safe_edges++;
+
+ if(debug_lvl >= DEBUG_SCARY_THINGS)
+ syslog(LOG_DEBUG, " Adding edge %s - %s weight %d", e->from.node->name, e->to.node->name, e->weight);
+
+ if(skipped)
+ {
+ next = edge_weight_tree->head;
+ continue;
+ }
+ }
+
+ if(debug_lvl >= DEBUG_SCARY_THINGS)
+ syslog(LOG_DEBUG, "Done, counted %d nodes and %d safe edges.", nodes, safe_edges);
+}
+
+/* Implementation of a simple breadth-first search algorithm.
+ Running time: O(E)
+*/
+
+void sssp_bfs(void)
+{
+ avl_node_t *node, *from, *next, *to;
+ edge_t *e;
+ node_t *n;
+ halfconnection_t to_hc, from_hc;
+ avl_tree_t *todo_tree;
+ int indirect;
+ char *name;
+
+ todo_tree = avl_alloc_tree(NULL, NULL);
+
+ /* Clear visited status on nodes */
+
+ for(node = node_tree->head; node; node = node->next)
+ {
+ n = (node_t *)node->data;
+ n->status.visited = 0;
+ n->status.indirect = 1;
+ }
+
+ /* Begin with myself */
+
+ myself->status.visited = 1;
+ myself->status.indirect = 0;
+ myself->nexthop = myself;
+ myself->via = myself;
+ node = avl_alloc_node();
+ node->data = myself;
+ avl_insert_top(todo_tree, node);
+
+ /* Loop while todo_tree is filled */
+
+ while(todo_tree->head)
+ {
+ for(from = todo_tree->head; from; from = next) /* "from" is the node from which we start */
+ {
+ next = from->next;
+ n = (node_t *)from->data;
+
+ for(to = n->edge_tree->head; to; to = to->next) /* "to" is the edge connected to "from" */
+ {
+ e = (edge_t *)to->data;
+
+ if(e->from.node == n) /* "from_hc" is the halfconnection with .node == from */
+ to_hc = e->to, from_hc = e->from;
+ else
+ to_hc = e->from, from_hc = e->to;
+
+ /* Situation:
+
+ /
+ /
+ ------(n)from_hc-----to_hc
+ \
+ \
+
+ n->address is set to the to_hc.udpaddress of the edge left of n.
+ We are currently examining the edge right of n:
+
+ - If from_hc.udpaddress != n->address, then to_hc.node is probably
+ not reachable for the nodes left of n. We do as if the indirectdata
+ flag is set on edge e.
+ - If edge e provides for better reachability of to_hc.node, update
+ to_hc.node and (re)add it to the todo_tree to (re)examine the reachability
+ of nodes behind it.
+ */
+
+ indirect = n->status.indirect || e->options & OPTION_INDIRECT || ((n != myself) && sockaddrcmp(&n->address, &from_hc.udpaddress));
+
+ if(to_hc.node->status.visited && (!to_hc.node->status.indirect || indirect))
+ continue;
+
+ to_hc.node->status.visited = 1;
+ to_hc.node->status.indirect = indirect;
+ to_hc.node->nexthop = (n->nexthop == myself) ? to_hc.node : n->nexthop;
+ to_hc.node->via = indirect ? n->via : to_hc.node;
+ to_hc.node->options = e->options;
+ if(sockaddrcmp(&to_hc.node->address, &to_hc.udpaddress))
+ {
+ node = avl_unlink(node_udp_tree, to_hc.node);
+ to_hc.node->address = to_hc.udpaddress;
+ if(to_hc.node->hostname)
+ free(to_hc.node->hostname);
+ to_hc.node->hostname = sockaddr2hostname(&to_hc.udpaddress);
+ avl_insert_node(node_udp_tree, node);
+ }
+ node = avl_alloc_node();
+ node->data = to_hc.node;
+ avl_insert_before(todo_tree, from, node);
+ }
+
+ avl_delete_node(todo_tree, from);
+ }
+ }
+
+ avl_free_tree(todo_tree);
+
+ /* Check reachability status. */
+
+ for(node = node_tree->head; node; node = next)
+ {
+ next = node->next;
+ n = (node_t *)node->data;
+
+ if(n->status.visited)
+ {
+ if(!n->status.reachable)
+ {
+ if(debug_lvl >= DEBUG_TRAFFIC)
+ syslog(LOG_DEBUG, _("Node %s (%s) became reachable"), n->name, n->hostname);
+ n->status.reachable = 1;
+ asprintf(&name, "hosts/%s-up", n->name);
+ execute_script(name);
+ free(name);
+ }
+ }
+ else
+ {
+ if(n->status.reachable)
+ {
+ if(debug_lvl >= DEBUG_TRAFFIC)
+ syslog(LOG_DEBUG, _("Node %s (%s) became unreachable"), n->name, n->hostname);
+ n->status.reachable = 0;
+ n->status.validkey = 0;
+ n->status.waitingforkey = 0;
+ n->sent_seqno = 0;
+ asprintf(&name, "hosts/%s-down", n->name);
+ execute_script(name);
+ free(name);
+ }
+ }
+ }
+}
+
+void graph(void)
+{
+ mst_kruskal();
+ sssp_bfs();
+}
--- /dev/null
+/*
+ graph.h -- header for graph.c
+ Copyright (C) 2001-2002 Guus Sliepen <guus@sliepen.warande.net>,
+ 2001-2002 Ivo Timmermans <itimmermans@bigfoot.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: graph.h,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+extern void graph(void);
+extern void mst_kruskal(void);
+extern void sssp_bfs(void);
--- /dev/null
+/*
+ meta.c -- handle the meta communication
+ Copyright (C) 2000-2002 Guus Sliepen <guus@sliepen.warande.net>,
+ 2000-2002 Ivo Timmermans <itimmermans@bigfoot.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: meta.c,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+#include "config.h"
+#include <utils.h>
+#include <avl_tree.h>
+
+#include <errno.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <string.h>
+/* This line must be below the rest for FreeBSD */
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <openssl/evp.h>
+
+#include "net.h"
+#include "connection.h"
+#include "system.h"
+#include "protocol.h"
+
+int send_meta(connection_t *c, char *buffer, int length)
+{
+ char *bufp;
+ int outlen;
+ char outbuf[MAXBUFSIZE];
+cp
+ if(debug_lvl >= DEBUG_META)
+ syslog(LOG_DEBUG, _("Sending %d bytes of metadata to %s (%s)"), length,
+ c->name, c->hostname);
+
+ if(c->status.encryptout)
+ {
+ EVP_EncryptUpdate(c->outctx, outbuf, &outlen, buffer, length);
+ bufp = outbuf;
+ length = outlen;
+ }
+ else
+ bufp = buffer;
+
+ if(write(c->socket, bufp, length) < 0)
+ {
+ syslog(LOG_ERR, _("Sending meta data to %s (%s) failed: %s"), c->name, c->hostname, strerror(errno));
+ return -1;
+ }
+cp
+ return 0;
+}
+
+void broadcast_meta(connection_t *from, char *buffer, int length)
+{
+ avl_node_t *node;
+ connection_t *c;
+cp
+ for(node = connection_tree->head; node; node = node->next)
+ {
+ c = (connection_t *)node->data;
+ if(c != from && c->status.active)
+ send_meta(c, buffer, length);
+ }
+cp
+}
+
+int receive_meta(connection_t *c)
+{
+ int x, l = sizeof(x);
+ int oldlen, i;
+ int lenin, reqlen;
+ int decrypted = 0;
+ char inbuf[MAXBUFSIZE];
+cp
+ if(getsockopt(c->socket, SOL_SOCKET, SO_ERROR, &x, &l) < 0)
+ {
+ syslog(LOG_ERR, _("This is a bug: %s:%d: %d:%s %s (%s)"), __FILE__, __LINE__, c->socket, strerror(errno),
+ c->name, c->hostname);
+ return -1;
+ }
+ if(x)
+ {
+ syslog(LOG_ERR, _("Metadata socket error for %s (%s): %s"),
+ c->name, c->hostname, strerror(x));
+ return -1;
+ }
+
+ /* Strategy:
+ - Read as much as possible from the TCP socket in one go.
+ - Decrypt it.
+ - Check if a full request is in the input buffer.
+ - If yes, process request and remove it from the buffer,
+ then check again.
+ - If not, keep stuff in buffer and exit.
+ */
+
+ lenin = read(c->socket, c->buffer + c->buflen, MAXBUFSIZE - c->buflen);
+
+ if(lenin<=0)
+ {
+ if(lenin==0)
+ {
+ if(debug_lvl >= DEBUG_CONNECTIONS)
+ syslog(LOG_NOTICE, _("Connection closed by %s (%s)"),
+ c->name, c->hostname);
+ }
+ else
+ if(errno==EINTR)
+ return 0;
+ else
+ syslog(LOG_ERR, _("Metadata socket read error for %s (%s): %s"),
+ c->name, c->hostname, strerror(errno));
+
+ return -1;
+ }
+
+ oldlen = c->buflen;
+ c->buflen += lenin;
+
+ while(lenin)
+ {
+ /* Decrypt */
+
+ if(c->status.decryptin && !decrypted)
+ {
+ EVP_DecryptUpdate(c->inctx, inbuf, &lenin, c->buffer + oldlen, lenin);
+ memcpy(c->buffer + oldlen, inbuf, lenin);
+ decrypted = 1;
+ }
+
+ /* Are we receiving a TCPpacket? */
+
+ if(c->tcplen)
+ {
+ if(c->tcplen <= c->buflen)
+ {
+ receive_tcppacket(c, c->buffer, c->tcplen);
+
+ c->buflen -= c->tcplen;
+ lenin -= c->tcplen;
+ memmove(c->buffer, c->buffer + c->tcplen, c->buflen);
+ oldlen = 0;
+ c->tcplen = 0;
+ continue;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ /* Otherwise we are waiting for a request */
+
+ reqlen = 0;
+
+ for(i = oldlen; i < c->buflen; i++)
+ {
+ if(c->buffer[i] == '\n')
+ {
+ c->buffer[i] = '\0'; /* replace end-of-line by end-of-string so we can use sscanf */
+ reqlen = i + 1;
+ break;
+ }
+ }
+
+ if(reqlen)
+ {
+ if(receive_request(c))
+ return -1;
+
+ c->buflen -= reqlen;
+ lenin -= reqlen;
+ memmove(c->buffer, c->buffer + reqlen, c->buflen);
+ oldlen = 0;
+ continue;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if(c->buflen >= MAXBUFSIZE)
+ {
+ syslog(LOG_ERR, _("Metadata read buffer overflow for %s (%s)"),
+ c->name, c->hostname);
+ return -1;
+ }
+
+ c->last_ping_time = now;
+cp
+ return 0;
+}
--- /dev/null
+/*
+ meta.h -- header for meta.c
+ Copyright (C) 2000-2002 Guus Sliepen <guus@sliepen.warande.net>,
+ 2000-2002 Ivo Timmermans <itimmermans@bigfoot.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: meta.h,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+#ifndef __TINC_META_H__
+#define __TINC_META_H__
+
+#include "connection.h"
+
+extern int send_meta(connection_t *, const char *, int);
+extern int broadcast_meta(connection_t *, const char *, int);
+extern int receive_meta(connection_t *);
+
+#endif /* __TINC_META_H__ */
/*
net.c -- most of the network code
- Copyright (C) 1998,1999,2000 Ivo Timmermans <itimmermans@bigfoot.com>,
- 2000 Guus Sliepen <guus@sliepen.warande.net>
+ Copyright (C) 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
+ 2000-2002 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- $Id: net.c,v 1.36 2000/10/18 20:12:08 zarq Exp $
+ $Id: net.c,v 1.37 2002/04/09 15:26:00 zarq Exp $
*/
#include "config.h"
-#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
+#ifdef HAVE_LINUX
+ #include <netinet/ip.h>
+ #include <netinet/tcp.h>
+#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/signal.h>
-#include <sys/socket.h>
+#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <syslog.h>
#include <unistd.h>
-
-#ifdef HAVE_TUNTAP
+#include <sys/ioctl.h>
+/* SunOS really wants sys/socket.h BEFORE net/if.h,
+ and FreeBSD wants these lines below the rest. */
+#include <arpa/inet.h>
+#include <sys/socket.h>
#include <net/if.h>
-#include LINUX_IF_TUN_H
-#endif
+
+#include <openssl/rand.h>
#include <utils.h>
#include <xalloc.h>
+#include <avl_tree.h>
+#include <list.h>
#include "conf.h"
-#include "encr.h"
+#include "connection.h"
+#include "meta.h"
#include "net.h"
#include "netutl.h"
+#include "process.h"
#include "protocol.h"
-#include "meta.h"
+#include "subnet.h"
+#include "graph.h"
+#include "process.h"
+#include "route.h"
+#include "device.h"
+#include "event.h"
#include "system.h"
-int tap_fd = -1;
-int taptype = 0;
-int total_tap_in = 0;
-int total_tap_out = 0;
-int total_socket_in = 0;
-int total_socket_out = 0;
-
-int upstreamindex = 0;
-static int seconds_till_retry;
-
-char *unknown = NULL;
-
-/*
- strip off the MAC adresses of an ethernet frame
-*/
-void strip_mac_addresses(vpn_packet_t *p)
-{
-cp
- memmove(p->data, p->data + 12, p->len -= 12);
-cp
-}
-
-/*
- reassemble MAC addresses
-*/
-void add_mac_addresses(vpn_packet_t *p)
-{
-cp
- memcpy(p->data + 12, p->data, p->len);
- p->len += 12;
- p->data[0] = p->data[6] = 0xfe;
- p->data[1] = p->data[7] = 0xfd;
- /* Really evil pointer stuff just below! */
- *((ip_t*)(&p->data[2])) = (ip_t)(htonl(myself->address));
- *((ip_t*)(&p->data[8])) = *((ip_t*)(&p->data[26]));
-cp
-}
-
-int xsend(conn_list_t *cl, vpn_packet_t *inpkt)
-{
- vpn_packet_t outpkt;
- int outlen, outpad;
-cp
- outpkt.len = inpkt->len;
- EVP_EncryptInit(cl->cipher_pktctx, cl->cipher_pkttype, cl->cipher_pktkey, NULL);
- EVP_EncryptUpdate(cl->cipher_pktctx, outpkt.data, &outlen, inpkt->data, inpkt->len);
- EVP_EncryptFinal(cl->cipher_pktctx, outpkt.data + outlen, &outpad);
- outlen += outpad;
-
- if(debug_lvl > 3)
- syslog(LOG_ERR, _("Sending packet of %d bytes to %s (%s)"),
- outlen, cl->name, cl->hostname);
+int do_purge = 0;
+int sighup = 0;
+int sigalrm = 0;
- total_socket_out += outlen;
+time_t now = 0;
- cl->want_ping = 1;
+/* Purge edges and subnets of unreachable nodes. Use carefully. */
- if((send(cl->socket, (char *) &(outpkt.len), outlen + 2, 0)) < 0)
- {
- syslog(LOG_ERR, _("Error sending packet to %s (%s): %m"),
- cl->name, cl->hostname);
- return -1;
- }
-cp
- return 0;
-}
-
-int xrecv(vpn_packet_t *inpkt)
+void purge(void)
{
- vpn_packet_t outpkt;
- int outlen, outpad;
-cp
- if(debug_lvl > 3)
- syslog(LOG_ERR, _("Receiving packet of %d bytes"),
- inpkt->len);
-
- outpkt.len = inpkt->len;
- EVP_DecryptInit(myself->cipher_pktctx, myself->cipher_pkttype, myself->cipher_pktkey, NULL);
- EVP_DecryptUpdate(myself->cipher_pktctx, outpkt.data, &outlen, inpkt->data, inpkt->len);
- /* FIXME: grok DecryptFinal
- EVP_DecryptFinal(myself->cipher_pktctx, outpkt.data + outlen, &outpad);
- */
-
- add_mac_addresses(&outpkt);
-
- if(write(tap_fd, outpkt.data, outpkt.len) < 0)
- syslog(LOG_ERR, _("Can't write to tap device: %m"));
- else
- total_tap_out += outpkt.len;
+ avl_node_t *nnode, *nnext, *enode, *enext, *snode, *snext, *cnode;
+ node_t *n;
+ edge_t *e;
+ subnet_t *s;
+ connection_t *c;
cp
- return 0;
-}
+ if(debug_lvl >= DEBUG_PROTOCOL)
+ syslog(LOG_DEBUG, _("Purging unreachable nodes"));
-/*
- add the given packet of size s to the
- queue q, be it the send or receive queue
-*/
-void add_queue(packet_queue_t **q, void *packet, size_t s)
-{
- queue_element_t *e;
-cp
- e = xmalloc(sizeof(*e));
- e->packet = xmalloc(s);
- memcpy(e->packet, packet, s);
-
- if(!*q)
- {
- *q = xmalloc(sizeof(**q));
- (*q)->head = (*q)->tail = NULL;
- }
-
- e->next = NULL; /* We insert at the tail */
+ for(nnode = node_tree->head; nnode; nnode = nnext)
+ {
+ nnext = nnode->next;
+ n = (node_t *)nnode->data;
- if((*q)->tail) /* Do we have a tail? */
+ if(!n->status.reachable)
{
- (*q)->tail->next = e;
- e->prev = (*q)->tail;
- }
- else /* No tail -> no head too */
- {
- (*q)->head = e;
- e->prev = NULL;
- }
-
- (*q)->tail = e;
-cp
-}
+ if(debug_lvl >= DEBUG_SCARY_THINGS)
+ syslog(LOG_DEBUG, _("Purging node %s (%s)"), n->name, n->hostname);
-/* Remove a queue element */
-void del_queue(packet_queue_t **q, queue_element_t *e)
-{
-cp
- free(e->packet);
+ for(snode = n->subnet_tree->head; snode; snode = snext)
+ {
+ snext = snode->next;
+ s = (subnet_t *)snode->data;
- if(e->next) /* There is a successor, so we are not tail */
- {
- if(e->prev) /* There is a predecessor, so we are not head */
+ for(cnode = connection_tree->head; cnode; cnode = cnode->next)
{
- e->next->prev = e->prev;
- e->prev->next = e->next;
+ c = (connection_t *)cnode->data;
+ if(c->status.active)
+ send_del_subnet(c, s);
}
- else /* We are head */
- {
- e->next->prev = NULL;
- (*q)->head = e->next;
- }
- }
- else /* We are tail (or all alone!) */
- {
- if(e->prev) /* We are not alone :) */
- {
- e->prev->next = NULL;
- (*q)->tail = e->prev;
- }
- else /* Adieu */
- {
- free(*q);
- *q = NULL;
- }
- }
-
- free(e);
-cp
-}
-
-/*
- flush a queue by calling function for
- each packet, and removing it when that
- returned a zero exit code
-*/
-void flush_queue(conn_list_t *cl, packet_queue_t **pq,
- int (*function)(conn_list_t*,void*))
-{
- queue_element_t *p, *next = NULL;
-cp
- for(p = (*pq)->head; p != NULL; )
- {
- next = p->next;
-
- if(!function(cl, p->packet))
- del_queue(pq, p);
-
- p = next;
- }
-
- if(debug_lvl > 3)
- syslog(LOG_DEBUG, _("Queue flushed"));
-cp
-}
-/*
- flush the send&recv queues
- void because nothing goes wrong here, packets
- remain in the queue if something goes wrong
-*/
-void flush_queues(conn_list_t *cl)
-{
-cp
- if(cl->sq)
- {
- if(debug_lvl > 3)
- syslog(LOG_DEBUG, _("Flushing send queue for %s (%s)"),
- cl->name, cl->hostname);
- flush_queue(cl, &(cl->sq), xsend);
- }
+ subnet_del(n, s);
+ }
- if(cl->rq)
- {
- if(debug_lvl > 3)
- syslog(LOG_DEBUG, _("Flushing receive queue for %s (%s)"),
- cl->name, cl->hostname);
- flush_queue(cl, &(cl->rq), xrecv);
- }
-cp
-}
+ for(enode = n->edge_tree->head; enode; enode = enext)
+ {
+ enext = enode->next;
+ e = (edge_t *)enode->data;
-/*
- send a packet to the given vpn ip.
-*/
-int send_packet(ip_t to, vpn_packet_t *packet)
-{
- conn_list_t *cl;
-cp
- if((cl = lookup_conn_list_ipv4(to)) == NULL)
- {
- if(debug_lvl > 3)
+ for(cnode = connection_tree->head; cnode; cnode = cnode->next)
{
- syslog(LOG_NOTICE, _("Trying to look up %d.%d.%d.%d in connection list failed!"),
- IP_ADDR_V(to));
+ c = (connection_t *)cnode->data;
+ if(c->status.active)
+ send_del_edge(c, e);
}
- return -1;
- }
-
- /* If we ourselves have indirectdata flag set, we should send only to our uplink! */
-
- /* FIXME - check for indirection and reprogram it The Right Way(tm) this time. */
-
- if(!cl->status.dataopen)
- if(setup_vpn_connection(cl) < 0)
- {
- syslog(LOG_ERR, _("Could not open UDP connection to %s (%s)"),
- cl->name, cl->hostname);
- return -1;
+ edge_del(e);
}
-
- if(!cl->status.validkey)
- {
- if(debug_lvl > 3)
- syslog(LOG_INFO, _("No valid key known yet for %s (%s), queueing packet"),
- cl->name, cl->hostname);
- add_queue(&(cl->sq), packet, packet->len + 2);
- if(!cl->status.waitingforkey)
- send_req_key(myself, cl); /* Keys should be sent to the host running the tincd */
- return 0;
- }
-
- if(!cl->status.active)
- {
- if(debug_lvl > 3)
- syslog(LOG_INFO, _("%s (%s) is not ready, queueing packet"),
- cl->name, cl->hostname);
- add_queue(&(cl->sq), packet, packet->len + 2);
- return 0; /* We don't want to mess up, do we? */
- }
-
- /* can we send it? can we? can we? huh? */
-cp
- return xsend(cl, packet);
-}
-/*
- open the local ethertap device
-*/
-int setup_tap_fd(void)
-{
- int nfd;
- const char *tapfname;
- config_t const *cfg;
-
-#ifdef HAVE_TUNTAP
- struct ifreq ifr;
-#endif
-cp
- if((cfg = get_config_val(config, tapdevice)))
- tapfname = cfg->data.ptr;
- else
-#ifdef HAVE_TUNTAP
- tapfname = "/dev/misc/net/tun";
-#else
- tapfname = "/dev/tap0";
-#endif
-cp
- if((nfd = open(tapfname, O_RDWR | O_NONBLOCK)) < 0)
- {
- syslog(LOG_ERR, _("Could not open %s: %m"), tapfname);
- return -1;
+ node_del(n);
}
-cp
- tap_fd = nfd;
-
- taptype = 0;
-
-#ifdef HAVE_TUNTAP
- /* Ok now check if this is an old ethertap or a new tun/tap thingie */
- memset(&ifr, 0, sizeof(ifr));
-cp
- ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
- if (netname)
- strncpy(ifr.ifr_name, netname, IFNAMSIZ);
-cp
- if (!ioctl(tap_fd, TUNSETIFF, (void *) &ifr))
- {
- syslog(LOG_INFO, _("%s is a new style tun/tap device"), tapfname);
- taptype = 1;
- if((cfg = get_config_val(config, tapsubnet)) == NULL)
- syslog(LOG_INFO, _("tun/tap device will be left unconfigured"));
- else
- /* Setup inetaddr/netmask etc */;
}
-#endif
-
-cp
- return 0;
-}
-
-/*
- set up the socket that we listen on for incoming
- (tcp) connections
-*/
-int setup_listen_meta_socket(int port)
-{
- int nfd, flags;
- struct sockaddr_in a;
- const int one = 1;
- config_t const *cfg;
-cp
- if((nfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
- {
- syslog(LOG_ERR, _("Creating metasocket failed: %m"));
- return -1;
- }
-
- if(setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)))
- {
- syslog(LOG_ERR, _("setsockopt: %m"));
- return -1;
- }
-
- if(setsockopt(nfd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one)))
- {
- syslog(LOG_ERR, _("setsockopt: %m"));
- return -1;
- }
-
- flags = fcntl(nfd, F_GETFL);
- if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0)
- {
- syslog(LOG_ERR, _("fcntl: %m"));
- return -1;
- }
-
- if((cfg = get_config_val(config, interface)))
- {
- if(setsockopt(nfd, SOL_SOCKET, SO_KEEPALIVE, cfg->data.ptr, strlen(cfg->data.ptr)))
- {
- syslog(LOG_ERR, _("Unable to bind listen socket to interface %s: %m"), cfg->data.ptr);
- return -1;
- }
- }
-
- memset(&a, 0, sizeof(a));
- a.sin_family = AF_INET;
- a.sin_port = htons(port);
-
- if((cfg = get_config_val(config, interfaceip)))
- a.sin_addr.s_addr = htonl(cfg->data.ip->ip);
- else
- a.sin_addr.s_addr = htonl(INADDR_ANY);
-
- if(bind(nfd, (struct sockaddr *)&a, sizeof(struct sockaddr)))
- {
- syslog(LOG_ERR, _("Can't bind to port %hd/tcp: %m"), port);
- return -1;
- }
-
- if(listen(nfd, 3))
- {
- syslog(LOG_ERR, _("listen: %m"));
- return -1;
- }
-cp
- return nfd;
-}
-
-/*
- setup the socket for incoming encrypted
- data (the udp part)
-*/
-int setup_vpn_in_socket(int port)
-{
- int nfd, flags;
- struct sockaddr_in a;
- const int one = 1;
cp
- if((nfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
- {
- syslog(LOG_ERR, _("Creating socket failed: %m"));
- return -1;
- }
-
- if(setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)))
- {
- syslog(LOG_ERR, _("setsockopt: %m"));
- return -1;
- }
-
- flags = fcntl(nfd, F_GETFL);
- if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0)
- {
- syslog(LOG_ERR, _("fcntl: %m"));
- return -1;
- }
-
- memset(&a, 0, sizeof(a));
- a.sin_family = AF_INET;
- a.sin_port = htons(port);
- a.sin_addr.s_addr = htonl(INADDR_ANY);
-
- if(bind(nfd, (struct sockaddr *)&a, sizeof(struct sockaddr)))
- {
- syslog(LOG_ERR, _("Can't bind to port %hd/udp: %m"), port);
- return -1;
- }
-cp
- return nfd;
}
/*
- setup an outgoing meta (tcp) socket
+ put all file descriptors in an fd_set array
+ While we're at it, purge stuff that needs to be removed.
*/
-int setup_outgoing_meta_socket(conn_list_t *cl)
+void build_fdset(fd_set *fs)
{
- int flags;
- struct sockaddr_in a;
- config_t const *cfg;
+ avl_node_t *node, *next;
+ connection_t *c;
+ int i;
cp
- if(debug_lvl > 0)
- syslog(LOG_INFO, _("Trying to connect to %s"), cl->hostname);
-
- if((cfg = get_config_val(cl->config, port)) == NULL)
- cl->port = 655;
- else
- cl->port = cfg->data.val;
-
- cl->meta_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if(cl->meta_socket == -1)
- {
- syslog(LOG_ERR, _("Creating socket for %s port %d failed: %m"),
- cl->hostname, cl->port);
- return -1;
- }
-
- a.sin_family = AF_INET;
- a.sin_port = htons(cl->port);
- a.sin_addr.s_addr = htonl(cl->address);
-
- if(connect(cl->meta_socket, (struct sockaddr *)&a, sizeof(a)) == -1)
- {
- syslog(LOG_ERR, _("%s port %hd: %m"), cl->hostname, cl->port);
- return -1;
- }
+ FD_ZERO(fs);
- flags = fcntl(cl->meta_socket, F_GETFL);
- if(fcntl(cl->meta_socket, F_SETFL, flags | O_NONBLOCK) < 0)
+ for(node = connection_tree->head; node; node = next)
{
- syslog(LOG_ERR, _("fcntl for %s port %d: %m"),
- cl->hostname, cl->port);
- return -1;
- }
+ next = node->next;
+ c = (connection_t *)node->data;
- if(debug_lvl > 0)
- syslog(LOG_INFO, _("Connected to %s port %hd"),
- cl->hostname, cl->port);
-
- cl->status.meta = 1;
-cp
- return 0;
-}
-
-/*
- setup an outgoing connection. It's not
- necessary to also open an udp socket as
- well, because the other host will initiate
- an authentication sequence during which
- we will do just that.
-*/
-int setup_outgoing_connection(char *name)
-{
- conn_list_t *ncn;
- struct hostent *h;
- config_t *cfg;
-cp
- if(check_id(name))
- {
- syslog(LOG_ERR, _("Invalid name for outgoing connection"));
- return -1;
+ if(c->status.remove)
+ connection_del(c);
+ else
+ FD_SET(c->socket, fs);
}
- ncn = new_conn_list();
- asprintf(&ncn->name, "%s", name);
-
- if(read_host_config(ncn))
- {
- syslog(LOG_ERR, _("Error reading host configuration file for %s"));
- free_conn_list(ncn);
- return -1;
- }
-
- if(!(cfg = get_config_val(ncn->config, address)))
- {
- syslog(LOG_ERR, _("No address specified for %s"));
- free_conn_list(ncn);
- return -1;
- }
-
- if(!(h = gethostbyname(cfg->data.ptr)))
- {
- syslog(LOG_ERR, _("Error looking up `%s': %m"), cfg->data.ptr);
- free_conn_list(ncn);
- return -1;
- }
+ if(!connection_tree->head)
+ purge();
- ncn->address = ntohl(*((ip_t*)(h->h_addr_list[0])));
- ncn->hostname = hostlookup(htonl(ncn->address));
-
- if(setup_outgoing_meta_socket(ncn) < 0)
+ for(i = 0; i < listen_sockets; i++)
{
- syslog(LOG_ERR, _("Could not set up a meta connection to %s"),
- ncn->hostname);
- free_conn_list(ncn);
- return -1;
+ FD_SET(listen_socket[i].tcp, fs);
+ FD_SET(listen_socket[i].udp, fs);
}
- ncn->status.outgoing = 1;
- ncn->buffer = xmalloc(MAXBUFSIZE);
- ncn->buflen = 0;
- ncn->last_ping_time = time(NULL);
- ncn->want_ping = 0;
-
- conn_list_add(ncn);
-
- send_id(ncn);
+ FD_SET(device_fd, fs);
cp
- return 0;
}
/*
- set up the local sockets (listen only)
+ Terminate a connection:
+ - Close the socket
+ - Remove associated edge and tell other connections about it if report = 1
+ - Check if we need to retry making an outgoing connection
+ - Deactivate the host
*/
-int setup_myself(void)
+void terminate_connection(connection_t *c, int report)
{
- config_t const *cfg;
+ avl_node_t *node;
+ connection_t *other;
cp
- myself = new_conn_list();
-
- asprintf(&myself->hostname, "MYSELF"); /* FIXME? Do hostlookup on ourselves? */
- myself->flags = 0;
- myself->protocol_version = PROT_CURRENT;
-
- if(!(cfg = get_config_val(config, tincname))) /* Not acceptable */
- {
- syslog(LOG_ERR, _("Name for tinc daemon required!"));
- return -1;
- }
- else
- asprintf(&myself->name, "%s", (char*)cfg->data.val);
-
- if(check_id(myself->name))
- {
- syslog(LOG_ERR, _("Invalid name for myself!"));
- return -1;
- }
-
- if(read_host_config(myself))
- {
- syslog(LOG_ERR, _("Cannot open host configuration file for myself!"));
- return -1;
- }
-
- if(!(cfg = get_config_val(myself->config, port)))
- myself->port = 655;
- else
- myself->port = cfg->data.val;
-
- if((cfg = get_config_val(myself->config, indirectdata)))
- if(cfg->data.val == stupid_true)
- myself->flags |= EXPORTINDIRECTDATA;
-
- if((cfg = get_config_val(myself->config, tcponly)))
- if(cfg->data.val == stupid_true)
- myself->flags |= TCPONLY;
-
- if((myself->meta_socket = setup_listen_meta_socket(myself->port)) < 0)
- {
- syslog(LOG_ERR, _("Unable to set up a listening socket!"));
- return -1;
- }
-
- if((myself->socket = setup_vpn_in_socket(myself->port)) < 0)
- {
- syslog(LOG_ERR, _("Unable to set up an incoming vpn data socket!"));
- close(myself->meta_socket);
- return -1;
- }
-
- myself->status.active = 1;
-
- syslog(LOG_NOTICE, _("Ready: listening on port %hd"), myself->port);
-cp
- return 0;
-}
-
-RETSIGTYPE
-sigalrm_handler(int a)
-{
- config_t const *cfg;
-cp
- cfg = get_next_config_val(config, connectto, upstreamindex++);
-
- if(!upstreamindex && !cfg)
- /* No upstream IP given, we're listen only. */
+ if(c->status.remove)
return;
- while(cfg)
- {
- if(!setup_outgoing_connection(cfg->data.ptr)) /* function returns 0 when there are no problems */
- {
- signal(SIGALRM, SIG_IGN);
- return;
- }
- cfg = get_next_config_val(config, connectto, upstreamindex++); /* Or else we try the next ConnectTo line */
- }
-
- signal(SIGALRM, sigalrm_handler);
- upstreamindex = 0;
- seconds_till_retry += 5;
- if(seconds_till_retry > MAXTIMEOUT) /* Don't wait more than MAXTIMEOUT seconds. */
- seconds_till_retry = MAXTIMEOUT;
- syslog(LOG_ERR, _("Still failed to connect to other, will retry in %d seconds"),
- seconds_till_retry);
- alarm(seconds_till_retry);
-cp
-}
-
-/*
- setup all initial network connections
-*/
-int setup_network_connections(void)
-{
- config_t const *cfg;
-cp
- if((cfg = get_config_val(config, pingtimeout)) == NULL)
- timeout = 5;
- else
- timeout = cfg->data.val;
+ if(debug_lvl >= DEBUG_CONNECTIONS)
+ syslog(LOG_NOTICE, _("Closing connection with %s (%s)"),
+ c->name, c->hostname);
- if(setup_tap_fd() < 0)
- return -1;
+ c->status.remove = 1;
+ c->status.active = 0;
- if(setup_myself() < 0)
- return -1;
+ if(c->node)
+ c->node->connection = NULL;
- if((cfg = get_next_config_val(config, connectto, upstreamindex++)) == NULL)
- /* No upstream IP given, we're listen only. */
- return 0;
+ if(c->socket)
+ close(c->socket);
- while(cfg)
+ if(c->edge)
{
- if(!setup_outgoing_connection(cfg->data.ptr)) /* function returns 0 when there are no problems */
- return 0;
- cfg = get_next_config_val(config, connectto, upstreamindex++); /* Or else we try the next ConnectTo line */
- }
-
- signal(SIGALRM, sigalrm_handler);
- upstreamindex = 0;
- seconds_till_retry = MAXTIMEOUT;
- syslog(LOG_NOTICE, _("Trying to re-establish outgoing connection in %d seconds"), seconds_till_retry);
- alarm(seconds_till_retry);
-cp
- return 0;
-}
-
-/*
- close all open network connections
-*/
-void close_network_connections(void)
-{
- conn_list_t *p;
-cp
- for(p = conn_list; p != NULL; p = p->next)
- {
- if(p->status.dataopen)
- {
- shutdown(p->socket, 0); /* No more receptions */
- close(p->socket);
- }
- if(p->status.meta)
- {
- send_termreq(p);
- shutdown(p->meta_socket, 0); /* No more receptions */
- close(p->meta_socket);
+ if(report)
+ {
+ for(node = connection_tree->head; node; node = node->next)
+ {
+ other = (connection_t *)node->data;
+ if(other->status.active && other != c)
+ send_del_edge(other, c->edge);
+ }
}
- }
-
- if(myself)
- if(myself->status.active)
- {
- close(myself->meta_socket);
- close(myself->socket);
- }
-
- close(tap_fd);
- destroy_conn_list();
- syslog(LOG_NOTICE, _("Terminating"));
-cp
- return;
-}
-
-/*
- create a data (udp) socket
-*/
-int setup_vpn_connection(conn_list_t *cl)
-{
- int nfd, flags;
- struct sockaddr_in a;
-cp
- if(debug_lvl > 0)
- syslog(LOG_DEBUG, _("Opening UDP socket to %s"), cl->hostname);
+ edge_del(c->edge);
- nfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- if(nfd == -1)
- {
- syslog(LOG_ERR, _("Creating UDP socket failed: %m"));
- return -1;
- }
-
- a.sin_family = AF_INET;
- a.sin_port = htons(cl->port);
- a.sin_addr.s_addr = htonl(cl->address);
-
- if(connect(nfd, (struct sockaddr *)&a, sizeof(a)) == -1)
- {
- syslog(LOG_ERR, _("Connecting to %s port %d failed: %m"),
- cl->hostname, cl->port);
- return -1;
- }
+ /* Run MST and SSSP algorithms */
- flags = fcntl(nfd, F_GETFL);
- if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0)
- {
- syslog(LOG_ERR, _("This is a bug: %s:%d: %d:%m %s (%s)"), __FILE__, __LINE__, nfd,
- cl->name, cl->hostname);
- return -1;
- }
-
- cl->socket = nfd;
- cl->status.dataopen = 1;
-cp
- return 0;
-}
-
-/*
- handle an incoming tcp connect call and open
- a connection to it.
-*/
-conn_list_t *create_new_connection(int sfd)
-{
- conn_list_t *p;
- struct sockaddr_in ci;
- int len = sizeof(ci);
-cp
- p = new_conn_list();
-
- if(getpeername(sfd, &ci, &len) < 0)
- {
- syslog(LOG_ERR, _("Error: getpeername: %m"));
- return NULL;
+ graph();
}
- p->name = unknown;
- p->address = ntohl(ci.sin_addr.s_addr);
- p->hostname = hostlookup(ci.sin_addr.s_addr);
- p->meta_socket = sfd;
- p->status.meta = 1;
- p->buffer = xmalloc(MAXBUFSIZE);
- p->buflen = 0;
- p->last_ping_time = time(NULL);
- p->want_ping = 0;
-
- if(debug_lvl > 0)
- syslog(LOG_NOTICE, _("Connection from %s port %d"),
- p->hostname, htons(ci.sin_port));
-
- p->allow_request = ID;
-cp
- return p;
-}
+ /* Check if this was our outgoing connection */
-/*
- put all file descriptors in an fd_set array
-*/
-void build_fdset(fd_set *fs)
-{
- conn_list_t *p;
-cp
- FD_ZERO(fs);
-
- for(p = conn_list; p != NULL; p = p->next)
+ if(c->outgoing)
{
- if(p->status.meta)
- FD_SET(p->meta_socket, fs);
- if(p->status.dataopen)
- FD_SET(p->socket, fs);
- }
-
- FD_SET(myself->meta_socket, fs);
- FD_SET(myself->socket, fs);
- FD_SET(tap_fd, fs);
-cp
-}
-
-/*
- receive incoming data from the listening
- udp socket and write it to the ethertap
- device after being decrypted
-*/
-int handle_incoming_vpn_data()
-{
- vpn_packet_t pkt;
- int lenin;
- int x, l = sizeof(x);
-cp
- if(getsockopt(myself->socket, SOL_SOCKET, SO_ERROR, &x, &l) < 0)
- {
- syslog(LOG_ERR, _("This is a bug: %s:%d: %d:%m"),
- __FILE__, __LINE__, myself->socket);
- return -1;
- }
- if(x)
- {
- syslog(LOG_ERR, _("Incoming data socket error: %s"), strerror(x));
- return -1;
- }
-
- if(recvfrom(myself->socket, (char *) &(pkt.len), MTU, 0, NULL, NULL) <= 0)
- {
- syslog(LOG_ERR, _("Receiving packet failed: %m"));
- return -1;
- }
-
-cp
- return xrecv(&pkt);
-}
-
-/*
- terminate a connection and notify the other
- end before closing the sockets
-*/
-void terminate_connection(conn_list_t *cl)
-{
- conn_list_t *p;
-
-cp
- if(cl->status.remove)
- return;
-
- if(debug_lvl > 0)
- syslog(LOG_NOTICE, _("Closing connection with %s (%s)"),
- cl->name, cl->hostname);
-
- if(cl->socket)
- close(cl->socket);
- if(cl->status.meta)
- close(cl->meta_socket);
-
- cl->status.remove = 1;
-
- /* If this cl isn't active, don't send any DEL_HOSTs. */
-
-/* FIXME: reprogram this.
- if(cl->status.active)
- notify_others(cl,NULL,send_del_host);
-*/
-
-cp
- /* Find all connections that were lost because they were behind cl
- (the connection that was dropped). */
- if(cl->status.meta)
- for(p = conn_list; p != NULL; p = p->next)
- {
- if((p->nexthop == cl) && (p != cl))
- {
- if(cl->status.active && p->status.active)
-/* FIXME: reprogram this
- notify_others(p,cl,send_del_host);
-*/;
- if(cl->socket)
- close(cl->socket);
- p->status.active = 0;
- p->status.remove = 1;
- }
- }
-
- cl->status.active = 0;
-
- if(cl->status.outgoing)
- {
- signal(SIGALRM, sigalrm_handler);
- seconds_till_retry = 5;
- alarm(seconds_till_retry);
- syslog(LOG_NOTICE, _("Trying to re-establish outgoing connection in 5 seconds"));
+ retry_outgoing(c->outgoing);
+ c->outgoing = NULL;
}
cp
}
end does not reply in time, we consider them dead
and close the connection.
*/
-int check_dead_connections(void)
+void check_dead_connections(void)
{
- conn_list_t *p;
- time_t now;
+ avl_node_t *node, *next;
+ connection_t *c;
cp
- now = time(NULL);
- for(p = conn_list; p != NULL; p = p->next)
+ for(node = connection_tree->head; node; node = next)
{
- if(p->status.remove)
- continue;
- if(p->status.active && p->status.meta)
- {
- if(p->last_ping_time + timeout < now)
+ next = node->next;
+ c = (connection_t *)node->data;
+ if(c->last_ping_time + pingtimeout < now)
+ {
+ if(c->status.active)
{
- if(p->status.pinged && !p->status.got_pong)
+ if(c->status.pinged)
{
- if(debug_lvl > 1)
- syslog(LOG_INFO, _("%s (%s) didn't respond to PING"),
- p->name, p->hostname);
- p->status.timeout = 1;
- terminate_connection(p);
+ if(debug_lvl >= DEBUG_PROTOCOL)
+ syslog(LOG_INFO, _("%s (%s) didn't respond to PING"),
+ c->name, c->hostname);
+ c->status.timeout = 1;
+ terminate_connection(c, 1);
}
- else if(p->want_ping)
+ else
{
- send_ping(p);
- p->last_ping_time = now;
- p->status.pinged = 1;
- p->status.got_pong = 0;
+ send_ping(c);
}
}
- }
- }
-cp
- return 0;
-}
-
-/*
- accept a new tcp connect and create a
- new connection
-*/
-int handle_new_meta_connection()
-{
- conn_list_t *ncn;
- struct sockaddr client;
- int nfd, len = sizeof(client);
-cp
- if((nfd = accept(myself->meta_socket, &client, &len)) < 0)
- {
- syslog(LOG_ERR, _("Accepting a new connection failed: %m"));
- return -1;
- }
-
- if(!(ncn = create_new_connection(nfd)))
- {
- shutdown(nfd, 2);
- close(nfd);
- syslog(LOG_NOTICE, _("Closed attempted connection"));
- return 0;
+ else
+ {
+ if(debug_lvl >= DEBUG_CONNECTIONS)
+ syslog(LOG_WARNING, _("Timeout from %s (%s) during authentication"),
+ c->name, c->hostname);
+ terminate_connection(c, 0);
+ }
+ }
}
-
- ncn->status.meta = 1;
- ncn->next = conn_list;
- conn_list = ncn;
cp
- return 0;
}
/*
*/
void check_network_activity(fd_set *f)
{
- conn_list_t *p;
- int x, l = sizeof(x);
+ connection_t *c;
+ avl_node_t *node;
+ int result, i;
+ int len = sizeof(result);
+ vpn_packet_t packet;
cp
- for(p = conn_list; p != NULL; p = p->next)
+ if(FD_ISSET(device_fd, f))
{
- if(p->status.remove)
- continue;
-
- if(p->status.dataopen)
- if(FD_ISSET(p->socket, f))
- {
- /*
- The only thing that can happen to get us here is apparently an
- error on this outgoing(!) UDP socket that isn't immediate (i.e.
- something that will not trigger an error directly on send()).
- I've once got here when it said `No route to host'.
- */
- getsockopt(p->socket, SOL_SOCKET, SO_ERROR, &x, &l);
- syslog(LOG_ERR, _("Outgoing data socket error for %s (%s): %s"),
- p->name, p->hostname, strerror(x));
- terminate_connection(p);
- return;
- }
-
- if(p->status.meta)
- if(FD_ISSET(p->meta_socket, f))
- if(receive_meta(p) < 0)
- {
- terminate_connection(p);
- return;
- }
+ if(!read_packet(&packet))
+ route_outgoing(&packet);
}
-
- if(FD_ISSET(myself->socket, f))
- handle_incoming_vpn_data();
- if(FD_ISSET(myself->meta_socket, f))
- handle_new_meta_connection();
-cp
-}
+ for(node = connection_tree->head; node; node = node->next)
+ {
+ c = (connection_t *)node->data;
-/*
- read, encrypt and send data that is
- available through the ethertap device
-*/
-void handle_tap_input(void)
-{
- vpn_packet_t vp;
- ip_t from, to;
- int ether_type, lenin;
-cp
- memset(&vp, 0, sizeof(vp));
+ if(c->status.remove)
+ continue;
- if(taptype = 1)
- {
- if((lenin = read(tap_fd, vp.data, MTU)) <= 0)
- {
- syslog(LOG_ERR, _("Error while reading from tapdevice: %m"));
- return;
- }
- vp.len = lenin;
- }
- else
- {
- if((lenin = read(tap_fd, &vp, MTU)) <= 0)
+ if(FD_ISSET(c->socket, f))
{
- syslog(LOG_ERR, _("Error while reading from tapdevice: %m"));
- return;
+ if(c->status.connecting)
+ {
+ c->status.connecting = 0;
+ getsockopt(c->socket, SOL_SOCKET, SO_ERROR, &result, &len);
+ if(!result)
+ finish_connecting(c);
+ else
+ {
+ if(debug_lvl >= DEBUG_CONNECTIONS)
+ syslog(LOG_DEBUG, _("Error while connecting to %s (%s): %s"), c->name, c->hostname, strerror(result));
+ close(c->socket);
+ do_outgoing_connection(c);
+ continue;
+ }
+ }
+ if(receive_meta(c) < 0)
+ {
+ terminate_connection(c, c->status.active);
+ continue;
+ }
}
- vp.len = lenin - 2;
}
- total_tap_in += lenin;
-
- ether_type = ntohs(*((unsigned short*)(&vp.data[12])));
- if(ether_type != 0x0800)
+ for(i = 0; i < listen_sockets; i++)
{
- if(debug_lvl > 3)
- syslog(LOG_INFO, _("Non-IP ethernet frame %04x from %02x:%02x:%02x:%02x:%02x:%02x"), ether_type, MAC_ADDR_V(vp.data[6]));
- return;
+ if(FD_ISSET(listen_socket[i].udp, f))
+ handle_incoming_vpn_data(listen_socket[i].udp);
+ if(FD_ISSET(listen_socket[i].tcp, f))
+ handle_new_meta_connection(listen_socket[i].tcp);
}
-
- if(lenin < 32)
- {
- if(debug_lvl > 3)
- syslog(LOG_INFO, _("Dropping short packet from %02x:%02x:%02x:%02x:%02x:%02x"), MAC_ADDR_V(vp.data[6]));
- return;
- }
-
- from = ntohl(*((unsigned long*)(&vp.data[26])));
- to = ntohl(*((unsigned long*)(&vp.data[30])));
-
- send_packet(to, &vp);
cp
}
struct timeval tv;
int r;
time_t last_ping_check;
+ event_t *event;
cp
- last_ping_check = time(NULL);
+ last_ping_check = now;
+
+ srand(now);
for(;;)
{
- tv.tv_sec = timeout;
+ now = time(NULL);
+
+ tv.tv_sec = 1 + (rand() & 7); /* Approx. 5 seconds, randomized to prevent global synchronisation effects */
tv.tv_usec = 0;
- prune_conn_list();
build_fdset(&fset);
if((r = select(FD_SETSIZE, &fset, NULL, NULL, &tv)) < 0)
{
- if(errno != EINTR) /* because of alarm */
+ if(errno != EINTR && errno != EAGAIN)
{
- syslog(LOG_ERR, _("Error while waiting for input: %m"));
+ syslog(LOG_ERR, _("Error while waiting for input: %s"), strerror(errno));
+ cp_trace();
+ dump_connections();
return;
}
+
+ continue;
}
- if(sighup)
+ check_network_activity(&fset);
+
+ if(do_purge)
{
- sighup = 0;
-/* FIXME: reprogram this.
- if(debug_lvl > 1)
- syslog(LOG_INFO, _("Rereading configuration file"));
- close_network_connections();
- clear_config();
- if(read_config_file(&config, configfilename))
+ purge();
+ do_purge = 0;
+ }
+
+ /* Let's check if everybody is still alive */
+
+ if(last_ping_check + pingtimeout < now)
+ {
+ check_dead_connections();
+ last_ping_check = now;
+
+ if(routing_mode== RMODE_SWITCH)
+ age_mac();
+
+ age_past_requests();
+
+ /* Should we regenerate our key? */
+
+ if(keyexpires < now)
{
- syslog(LOG_ERR, _("Unable to reread configuration file, exiting"));
- exit(0);
+ if(debug_lvl >= DEBUG_STATUS)
+ syslog(LOG_INFO, _("Regenerating symmetric key"));
+
+ RAND_pseudo_bytes(myself->key, myself->keylength);
+ send_key_changed(myself->connection, myself);
+ keyexpires = now + keylifetime;
}
- sleep(5);
- setup_network_connections();
-*/
- continue;
}
- if(last_ping_check + timeout < time(NULL))
- /* Let's check if everybody is still alive */
- {
- check_dead_connections();
- last_ping_check = time(NULL);
- }
- if(r > 0)
+ while((event = get_expired_event()))
+ {
+ event->handler(event->data);
+ free(event);
+ }
+
+ if(sigalrm)
+ {
+ syslog(LOG_INFO, _("Flushing event queue"));
+
+ while(event_tree->head)
+ {
+ event = (event_t *)event_tree->head->data;
+ event->handler(event->data);
+ event_del(event);
+ }
+ sigalrm = 0;
+ }
+
+ if(sighup)
{
- check_network_activity(&fset);
+ sighup = 0;
+ close_network_connections();
+ exit_configuration(&config_tree);
+
+ syslog(LOG_INFO, _("Rereading configuration file and restarting in 5 seconds..."));
+ sleep(5);
- /* local tap data */
- if(FD_ISSET(tap_fd, &fset))
- handle_tap_input();
+ init_configuration(&config_tree);
+
+ if(read_server_config())
+ {
+ syslog(LOG_ERR, _("Unable to reread configuration file, exitting."));
+ exit(1);
+ }
+
+ if(setup_network_connections())
+ return;
+
+ continue;
}
}
cp
/*
net.h -- header for net.c
- Copyright (C) 1998,1999,2000 Ivo Timmermans <zarq@iname.com>
+ Copyright (C) 1998-2002 Ivo Timmermans <zarq@iname.com>
+ 2000-2002 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- $Id: net.h,v 1.10 2000/10/18 20:12:09 zarq Exp $
+ $Id: net.h,v 1.11 2002/04/09 15:26:00 zarq Exp $
*/
#ifndef __TINC_NET_H__
#define __TINC_NET_H__
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
#include <sys/time.h>
#include "config.h"
-#define MAXSIZE 1700 /* should be a bit more than the MTU for the tapdevice */
-#define MTU 1600
-
-#define MAC_ADDR_S "%02x:%02x:%02x:%02x:%02x:%02x"
-#define MAC_ADDR_V(x) ((unsigned char*)&(x))[0],((unsigned char*)&(x))[1], \
- ((unsigned char*)&(x))[2],((unsigned char*)&(x))[3], \
- ((unsigned char*)&(x))[4],((unsigned char*)&(x))[5]
-
-#define IP_ADDR_S "%d.%d.%d.%d"
-
-#ifdef WORDS_BIGENDIAN
-# define IP_ADDR_V(x) ((unsigned char*)&(x))[0],((unsigned char*)&(x))[1], \
- ((unsigned char*)&(x))[2],((unsigned char*)&(x))[3]
+#ifdef ENABLE_JUMBOGRAMS
+ #define MTU 9014 /* 9000 bytes payload + 14 bytes ethernet header */
+ #define MAXSIZE 9100 /* MTU + header (seqno) and trailer (CBC padding and HMAC) */
+ #define MAXBUFSIZE 9100 /* Must support TCP packets of length 9000. */
#else
-# define IP_ADDR_V(x) ((unsigned char*)&(x))[3],((unsigned char*)&(x))[2], \
- ((unsigned char*)&(x))[1],((unsigned char*)&(x))[0]
+ #define MTU 1514 /* 1500 bytes payload + 14 bytes ethernet header */
+ #define MAXSIZE 1600 /* MTU + header (seqno) and trailer (CBC padding and HMAC) */
+ #define MAXBUFSIZE 2100 /* Quite large but needed for support of keys up to 8192 bits. */
#endif
-#define MAXBUFSIZE 4096 /* Probably way too much, but it must fit every possible request. */
+#define MAXSOCKETS 128 /* Overkill... */
-/* flags */
-#define INDIRECTDATA 0x0001 /* Used to indicate that this host has to be reached indirect */
-#define EXPORTINDIRECTDATA 0x0002 /* Used to indicate uplink that it has to tell others to do INDIRECTDATA */
-#define TCPONLY 0x0004 /* Tells sender to send packets over TCP instead of UDP (for firewalls) */
+#define MAXQUEUELENGTH 8 /* Maximum number of packats in a single queue */
typedef struct mac_t
{
unsigned char x[6];
} mac_t;
-typedef unsigned long ipv4_t;
+typedef struct ipv4_t
+{
+ unsigned char x[4];
+} ipv4_t;
-typedef ipv4_t ip_t; /* alias for ipv4_t */
+typedef struct ip_mask_t {
+ ipv4_t address;
+ ipv4_t mask;
+} ip_mask_t;
typedef struct ipv6_t
{
typedef short length_t;
+typedef union {
+ struct sockaddr sa;
+ struct sockaddr_in in;
+ struct sockaddr_in6 in6;
+} sockaddr_t;
+
+#ifdef SA_LEN
+#define SALEN(s) SA_LEN(&s)
+#else
+#define SALEN(s) (s.sa_family==AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6))
+#endif
+
typedef struct vpn_packet_t {
- length_t len; /* the actual number of bytes in the `data' field */
+ length_t len; /* the actual number of bytes in the `data' field */
+ int priority; /* priority or TOS */
+ unsigned int seqno; /* 32 bits sequence number (network byte order of course) */
unsigned char data[MAXSIZE];
} vpn_packet_t;
-typedef struct passphrase_t {
- unsigned short len;
- unsigned char *phrase;
-} passphrase_t;
-
-typedef struct status_bits_t {
- int pinged:1; /* sent ping */
- int got_pong:1; /* received pong */
- int meta:1; /* meta connection exists */
- int active:1; /* 1 if active.. */
- int outgoing:1; /* I myself asked for this conn */
- int termreq:1; /* the termination of this connection was requested */
- int remove:1; /* Set to 1 if you want this connection removed */
- int timeout:1; /* 1 if gotten timeout */
- int validkey:1; /* 1 if we currently have a valid key for him */
- int waitingforkey:1; /* 1 if we already sent out a request */
- int dataopen:1; /* 1 if we have a valid UDP connection open */
- int encryptout:1; /* 1 if we can encrypt outgoing traffic */
- int decryptin:1; /* 1 if we have to decrypt incoming traffic */
- int unused:18;
-} status_bits_t;
-
-typedef struct option_bits_t {
- int unused:32;
-} option_bits_t;
-
typedef struct queue_element_t {
void *packet;
struct queue_element_t *prev;
queue_element_t *tail;
} packet_queue_t;
-typedef struct enc_key_t {
- int length;
- char *key;
- time_t expiry;
-} enc_key_t;
-
-extern int tap_fd;
-
-extern int total_tap_in;
-extern int total_tap_out;
-extern int total_socket_in;
-extern int total_socket_out;
-
-extern char *unknown;
-
-extern char *request_name[256];
-extern char *status_text[10];
-
-#include "connlist.h" /* Yes, very strange placement indeed, but otherwise the typedefs get all tangled up */
-
-extern int str2opt(const char *);
-extern char *opt2str(int);
-extern int send_packet(ip_t, vpn_packet_t *);
+typedef struct outgoing_t {
+ char *name;
+ int timeout;
+ struct config_t *cfg;
+ struct addrinfo *ai;
+ struct addrinfo *aip;
+} outgoing_t;
+
+typedef struct listen_socket_t {
+ int tcp;
+ int udp;
+ sockaddr_t sa;
+} listen_socket_t;
+
+extern int maxtimeout;
+extern int seconds_till_retry;
+extern int addressfamily;
+
+extern char *request_name[];
+extern char *status_text[];
+
+#include "connection.h" /* Yes, very strange placement indeed, but otherwise the typedefs get all tangled up */
+
+extern listen_socket_t listen_socket[MAXSOCKETS];
+extern int listen_sockets;
+extern int keyexpires;
+extern int keylifetime;
+extern int do_prune;
+extern int do_purge;
+extern char *myport;
+extern time_t now;
+
+extern void retry_outgoing(outgoing_t *);
+extern void handle_incoming_vpn_data(int);
+extern void finish_connecting(connection_t *);
+extern void do_outgoing_connection(connection_t *);
+extern int handle_new_meta_connection(int);
+extern int setup_listen_socket(sockaddr_t *);
+extern int setup_vpn_in_socket(sockaddr_t *);
+extern void send_packet(struct node_t *, vpn_packet_t *);
+extern void receive_packet(struct node_t *, vpn_packet_t *);
+extern void receive_tcppacket(struct connection_t *, char *, int);
+extern void broadcast_packet(struct node_t *, vpn_packet_t *);
extern int setup_network_connections(void);
+extern void setup_outgoing_connection(struct outgoing_t *);
+extern void try_outgoing_connections(void);
extern void close_network_connections(void);
extern void main_loop(void);
-extern int setup_vpn_connection(conn_list_t *);
-extern void terminate_connection(conn_list_t *);
-extern void flush_queues(conn_list_t *);
-extern int xrecv(vpn_packet_t *);
-extern void add_queue(packet_queue_t **, void *, size_t);
+extern void terminate_connection(connection_t *, int);
+extern void flush_queue(struct node_t *);
+extern int read_rsa_public_key(struct connection_t *);
#endif /* __TINC_NET_H__ */
--- /dev/null
+/*
+ net_packet.c -- Handles in- and outgoing VPN packets
+ Copyright (C) 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
+ 2000-2002 Guus Sliepen <guus@sliepen.warande.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: net_packet.c,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+#include "config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#ifdef HAVE_LINUX
+ #include <netinet/ip.h>
+ #include <netinet/tcp.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+/* SunOS really wants sys/socket.h BEFORE net/if.h,
+ and FreeBSD wants these lines below the rest. */
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+#include <openssl/rand.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/hmac.h>
+
+#ifndef HAVE_RAND_PSEUDO_BYTES
+#define RAND_pseudo_bytes RAND_bytes
+#endif
+
+#include <zlib.h>
+
+#include <utils.h>
+#include <xalloc.h>
+#include <avl_tree.h>
+#include <list.h>
+
+#include "conf.h"
+#include "connection.h"
+#include "meta.h"
+#include "net.h"
+#include "netutl.h"
+#include "process.h"
+#include "protocol.h"
+#include "subnet.h"
+#include "graph.h"
+#include "process.h"
+#include "route.h"
+#include "device.h"
+#include "event.h"
+
+#include "system.h"
+
+int keylifetime = 0;
+int keyexpires = 0;
+
+#define MAX_SEQNO 1073741824
+
+/* VPN packet I/O */
+
+void receive_udppacket(node_t *n, vpn_packet_t *inpkt)
+{
+ vpn_packet_t pkt1, pkt2;
+ vpn_packet_t *pkt[] = {&pkt1, &pkt2, &pkt1, &pkt2};
+ int nextpkt = 0;
+ vpn_packet_t *outpkt = pkt[0];
+ int outlen, outpad;
+ long int complen = MTU + 12;
+ EVP_CIPHER_CTX ctx;
+ char hmac[EVP_MAX_MD_SIZE];
+cp
+ /* Check the message authentication code */
+
+ if(myself->digest && myself->maclength)
+ {
+ inpkt->len -= myself->maclength;
+ HMAC(myself->digest, myself->key, myself->keylength, (char *)&inpkt->seqno, inpkt->len, hmac, NULL);
+ if(memcmp(hmac, (char *)&inpkt->seqno + inpkt->len, myself->maclength))
+ {
+ if(debug_lvl >= DEBUG_TRAFFIC)
+ syslog(LOG_DEBUG, _("Got unauthenticated packet from %s (%s)"), n->name, n->hostname);
+ return;
+ }
+ }
+
+ /* Decrypt the packet */
+
+ if(myself->cipher)
+ {
+ outpkt = pkt[nextpkt++];
+
+ EVP_DecryptInit(&ctx, myself->cipher, myself->key, myself->key + myself->cipher->key_len);
+ EVP_DecryptUpdate(&ctx, (char *)&outpkt->seqno, &outlen, (char *)&inpkt->seqno, inpkt->len);
+ EVP_DecryptFinal(&ctx, (char *)&outpkt->seqno + outlen, &outpad);
+
+ outpkt->len = outlen + outpad;
+ inpkt = outpkt;
+ }
+
+ /* Check the sequence number */
+
+ inpkt->len -= sizeof(inpkt->seqno);
+ inpkt->seqno = ntohl(inpkt->seqno);
+
+ if(inpkt->seqno <= n->received_seqno)
+ {
+ if(debug_lvl >= DEBUG_TRAFFIC)
+ syslog(LOG_DEBUG, _("Got late or replayed packet from %s (%s), seqno %d"), n->name, n->hostname, inpkt->seqno);
+ return;
+ }
+
+ n->received_seqno = inpkt->seqno;
+
+ if(n->received_seqno > MAX_SEQNO)
+ keyexpires = 0;
+
+ /* Decompress the packet */
+
+ if(myself->compression)
+ {
+ outpkt = pkt[nextpkt++];
+
+ if(uncompress(outpkt->data, &complen, inpkt->data, inpkt->len) != Z_OK)
+ {
+ syslog(LOG_ERR, _("Error while uncompressing packet from %s (%s)"), n->name, n->hostname);
+ return;
+ }
+
+ outpkt->len = complen;
+ inpkt = outpkt;
+ }
+
+ receive_packet(n, inpkt);
+cp
+}
+
+void receive_tcppacket(connection_t *c, char *buffer, int len)
+{
+ vpn_packet_t outpkt;
+cp
+ outpkt.len = len;
+ memcpy(outpkt.data, buffer, len);
+
+ receive_packet(c->node, &outpkt);
+cp
+}
+
+void receive_packet(node_t *n, vpn_packet_t *packet)
+{
+cp
+ if(debug_lvl >= DEBUG_TRAFFIC)
+ syslog(LOG_DEBUG, _("Received packet of %d bytes from %s (%s)"), packet->len, n->name, n->hostname);
+
+ route_incoming(n, packet);
+cp
+}
+
+void send_udppacket(node_t *n, vpn_packet_t *inpkt)
+{
+ vpn_packet_t pkt1, pkt2;
+ vpn_packet_t *pkt[] = {&pkt1, &pkt2, &pkt1, &pkt2};
+ int nextpkt = 0;
+ vpn_packet_t *outpkt;
+ int origlen;
+ int outlen, outpad;
+ long int complen = MTU + 12;
+ EVP_CIPHER_CTX ctx;
+ vpn_packet_t *copy;
+ static int priority = 0;
+ int origpriority;
+ int sock;
+cp
+ /* Make sure we have a valid key */
+
+ if(!n->status.validkey)
+ {
+ if(debug_lvl >= DEBUG_TRAFFIC)
+ syslog(LOG_INFO, _("No valid key known yet for %s (%s), queueing packet"),
+ n->name, n->hostname);
+
+ /* Since packet is on the stack of handle_tap_input(),
+ we have to make a copy of it first. */
+
+ copy = xmalloc(sizeof(vpn_packet_t));
+ memcpy(copy, inpkt, sizeof(vpn_packet_t));
+
+ list_insert_tail(n->queue, copy);
+
+ if(n->queue->count > MAXQUEUELENGTH)
+ list_delete_head(n->queue);
+
+ if(!n->status.waitingforkey)
+ send_req_key(n->nexthop->connection, myself, n);
+
+ n->status.waitingforkey = 1;
+
+ return;
+ }
+
+ origlen = inpkt->len;
+ origpriority = inpkt->priority;
+
+ /* Compress the packet */
+
+ if(n->compression)
+ {
+ outpkt = pkt[nextpkt++];
+
+ if(compress2(outpkt->data, &complen, inpkt->data, inpkt->len, n->compression) != Z_OK)
+ {
+ syslog(LOG_ERR, _("Error while compressing packet to %s (%s)"), n->name, n->hostname);
+ return;
+ }
+
+ outpkt->len = complen;
+ inpkt = outpkt;
+ }
+
+ /* Add sequence number */
+
+ inpkt->seqno = htonl(++(n->sent_seqno));
+ inpkt->len += sizeof(inpkt->seqno);
+
+ /* Encrypt the packet */
+
+ if(n->cipher)
+ {
+ outpkt = pkt[nextpkt++];
+
+ EVP_EncryptInit(&ctx, n->cipher, n->key, n->key + n->cipher->key_len);
+ EVP_EncryptUpdate(&ctx, (char *)&outpkt->seqno, &outlen, (char *)&inpkt->seqno, inpkt->len);
+ EVP_EncryptFinal(&ctx, (char *)&outpkt->seqno + outlen, &outpad);
+
+ outpkt->len = outlen + outpad;
+ inpkt = outpkt;
+ }
+
+ /* Add the message authentication code */
+
+ if(n->digest && n->maclength)
+ {
+ HMAC(n->digest, n->key, n->keylength, (char *)&inpkt->seqno, inpkt->len, (char *)&inpkt->seqno + inpkt->len, &outlen);
+ inpkt->len += n->maclength;
+ }
+
+ /* Determine which socket we have to use */
+
+ for(sock = 0; sock < listen_sockets; sock++)
+ if(n->address.sa.sa_family == listen_socket[sock].sa.sa.sa_family)
+ break;
+
+ if(sock >= listen_sockets)
+ sock = 0; /* If none is available, just use the first and hope for the best. */
+
+ /* Send the packet */
+
+#if defined(SOL_IP) && defined(IP_TOS)
+ if(priorityinheritance && origpriority != priority && listen_socket[sock].sa.sa.sa_family == AF_INET)
+ {
+ priority = origpriority;
+ if(debug_lvl >= DEBUG_TRAFFIC)
+ syslog(LOG_DEBUG, _("Setting outgoing packet priority to %d"), priority);
+ if(setsockopt(sock, SOL_IP, IP_TOS, &priority, sizeof(priority))) /* SO_PRIORITY doesn't seem to work */
+ syslog(LOG_ERR, _("System call `%s' failed: %s"), "setsockopt", strerror(errno));
+ }
+#endif
+
+ if((sendto(listen_socket[sock].udp, (char *)&inpkt->seqno, inpkt->len, 0, &(n->address.sa), SALEN(n->address.sa))) < 0)
+ {
+ syslog(LOG_ERR, _("Error sending packet to %s (%s): %s"),
+ n->name, n->hostname, strerror(errno));
+ return;
+ }
+
+ inpkt->len = origlen;
+cp
+}
+
+/*
+ send a packet to the given vpn ip.
+*/
+void send_packet(node_t *n, vpn_packet_t *packet)
+{
+ node_t *via;
+cp
+ if(debug_lvl >= DEBUG_TRAFFIC)
+ syslog(LOG_ERR, _("Sending packet of %d bytes to %s (%s)"),
+ packet->len, n->name, n->hostname);
+
+ if(n == myself)
+ {
+ if(debug_lvl >= DEBUG_TRAFFIC)
+ {
+ syslog(LOG_NOTICE, _("Packet is looping back to us!"));
+ }
+
+ return;
+ }
+
+ if(!n->status.reachable)
+ {
+ if(debug_lvl >= DEBUG_TRAFFIC)
+ syslog(LOG_INFO, _("Node %s (%s) is not reachable"),
+ n->name, n->hostname);
+ return;
+ }
+
+ via = (n->via == myself)?n->nexthop:n->via;
+
+ if(via != n && debug_lvl >= DEBUG_TRAFFIC)
+ syslog(LOG_ERR, _("Sending packet to %s via %s (%s)"),
+ n->name, via->name, n->via->hostname);
+
+ if((myself->options | via->options) & OPTION_TCPONLY)
+ {
+ if(send_tcppacket(via->connection, packet))
+ terminate_connection(via->connection, 1);
+ }
+ else
+ send_udppacket(via, packet);
+}
+
+/* Broadcast a packet using the minimum spanning tree */
+
+void broadcast_packet(node_t *from, vpn_packet_t *packet)
+{
+ avl_node_t *node;
+ connection_t *c;
+cp
+ if(debug_lvl >= DEBUG_TRAFFIC)
+ syslog(LOG_INFO, _("Broadcasting packet of %d bytes from %s (%s)"),
+ packet->len, from->name, from->hostname);
+
+ for(node = connection_tree->head; node; node = node->next)
+ {
+ c = (connection_t *)node->data;
+ if(c->status.active && c->status.mst && c != from->nexthop->connection)
+ send_packet(c->node, packet);
+ }
+cp
+}
+
+void flush_queue(node_t *n)
+{
+ list_node_t *node, *next;
+cp
+ if(debug_lvl >= DEBUG_TRAFFIC)
+ syslog(LOG_INFO, _("Flushing queue for %s (%s)"), n->name, n->hostname);
+
+ for(node = n->queue->head; node; node = next)
+ {
+ next = node->next;
+ send_udppacket(n, (vpn_packet_t *)node->data);
+ list_delete_node(n->queue, node);
+ }
+cp
+}
+
+void handle_incoming_vpn_data(int sock)
+{
+ vpn_packet_t pkt;
+ int x, l = sizeof(x);
+ char *hostname;
+ sockaddr_t from;
+ socklen_t fromlen = sizeof(from);
+ node_t *n;
+cp
+ if(getsockopt(sock, SOL_SOCKET, SO_ERROR, &x, &l) < 0)
+ {
+ syslog(LOG_ERR, _("This is a bug: %s:%d: %d:%s"),
+ __FILE__, __LINE__, sock, strerror(errno));
+ cp_trace();
+ exit(1);
+ }
+ if(x)
+ {
+ syslog(LOG_ERR, _("Incoming data socket error: %s"), strerror(x));
+ return;
+ }
+
+ if((pkt.len = recvfrom(sock, (char *)&pkt.seqno, MAXSIZE, 0, &from.sa, &fromlen)) <= 0)
+ {
+ syslog(LOG_ERR, _("Receiving packet failed: %s"), strerror(errno));
+ return;
+ }
+
+ sockaddrunmap(&from); /* Some braindead IPv6 implementations do stupid things. */
+
+ n = lookup_node_udp(&from);
+
+ if(!n)
+ {
+ hostname = sockaddr2hostname(&from);
+ syslog(LOG_WARNING, _("Received UDP packet from unknown source %s"), hostname);
+ free(hostname);
+ return;
+ }
+
+ if(n->connection)
+ n->connection->last_ping_time = now;
+
+ receive_udppacket(n, &pkt);
+cp
+}
+
--- /dev/null
+/*
+ net_setup.c -- Setup.
+ Copyright (C) 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
+ 2000-2002 Guus Sliepen <guus@sliepen.warande.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: net_setup.c,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+#include "config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#ifdef HAVE_LINUX
+ #include <netinet/ip.h>
+ #include <netinet/tcp.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+/* SunOS really wants sys/socket.h BEFORE net/if.h,
+ and FreeBSD wants these lines below the rest. */
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+#include <openssl/rand.h>
+
+#include <utils.h>
+#include <xalloc.h>
+#include <avl_tree.h>
+#include <list.h>
+
+#include "conf.h"
+#include "connection.h"
+#include "meta.h"
+#include "net.h"
+#include "netutl.h"
+#include "process.h"
+#include "protocol.h"
+#include "subnet.h"
+#include "graph.h"
+#include "process.h"
+#include "route.h"
+#include "device.h"
+#include "event.h"
+
+#include "system.h"
+
+char *myport;
+
+int read_rsa_public_key(connection_t *c)
+{
+ FILE *fp;
+ char *fname;
+ char *key;
+cp
+ if(!c->rsa_key)
+ c->rsa_key = RSA_new();
+
+ /* First, check for simple PublicKey statement */
+
+ if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &key))
+ {
+ BN_hex2bn(&c->rsa_key->n, key);
+ BN_hex2bn(&c->rsa_key->e, "FFFF");
+ free(key);
+ return 0;
+ }
+
+ /* Else, check for PublicKeyFile statement and read it */
+
+ if(get_config_string(lookup_config(c->config_tree, "PublicKeyFile"), &fname))
+ {
+ if(is_safe_path(fname))
+ {
+ if((fp = fopen(fname, "r")) == NULL)
+ {
+ syslog(LOG_ERR, _("Error reading RSA public key file `%s': %s"),
+ fname, strerror(errno));
+ free(fname);
+ return -1;
+ }
+ free(fname);
+ c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL);
+ fclose(fp);
+ if(!c->rsa_key)
+ {
+ syslog(LOG_ERR, _("Reading RSA public key file `%s' failed: %s"),
+ fname, strerror(errno));
+ return -1;
+ }
+ return 0;
+ }
+ else
+ {
+ free(fname);
+ return -1;
+ }
+ }
+
+ /* Else, check if a harnessed public key is in the config file */
+
+ asprintf(&fname, "%s/hosts/%s", confbase, c->name);
+ if((fp = fopen(fname, "r")))
+ {
+ c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL);
+ fclose(fp);
+ }
+
+ free(fname);
+
+ if(c->rsa_key)
+ return 0;
+ else
+ {
+ syslog(LOG_ERR, _("No public key for %s specified!"), c->name);
+ return -1;
+ }
+}
+
+int read_rsa_private_key(void)
+{
+ FILE *fp;
+ char *fname, *key;
+cp
+ if(get_config_string(lookup_config(config_tree, "PrivateKey"), &key))
+ {
+ myself->connection->rsa_key = RSA_new();
+ BN_hex2bn(&myself->connection->rsa_key->d, key);
+ BN_hex2bn(&myself->connection->rsa_key->e, "FFFF");
+ free(key);
+ return 0;
+ }
+
+ if(!get_config_string(lookup_config(config_tree, "PrivateKeyFile"), &fname))
+ asprintf(&fname, "%s/rsa_key.priv", confbase);
+
+ if(is_safe_path(fname))
+ {
+ if((fp = fopen(fname, "r")) == NULL)
+ {
+ syslog(LOG_ERR, _("Error reading RSA private key file `%s': %s"),
+ fname, strerror(errno));
+ free(fname);
+ return -1;
+ }
+ free(fname);
+ myself->connection->rsa_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
+ fclose(fp);
+ if(!myself->connection->rsa_key)
+ {
+ syslog(LOG_ERR, _("Reading RSA private key file `%s' failed: %s"),
+ fname, strerror(errno));
+ return -1;
+ }
+ return 0;
+ }
+
+ free(fname);
+ return -1;
+}
+
+/*
+ Configure node_t myself and set up the local sockets (listen only)
+*/
+int setup_myself(void)
+{
+ config_t *cfg;
+ subnet_t *subnet;
+ char *name, *hostname, *mode, *afname, *cipher, *digest;
+ struct addrinfo hint, *ai, *aip;
+ int choice, err;
+cp
+ myself = new_node();
+ myself->connection = new_connection();
+ init_configuration(&myself->connection->config_tree);
+
+ asprintf(&myself->hostname, _("MYSELF"));
+ asprintf(&myself->connection->hostname, _("MYSELF"));
+
+ myself->connection->options = 0;
+ myself->connection->protocol_version = PROT_CURRENT;
+
+ if(!get_config_string(lookup_config(config_tree, "Name"), &name)) /* Not acceptable */
+ {
+ syslog(LOG_ERR, _("Name for tinc daemon required!"));
+ return -1;
+ }
+
+ if(check_id(name))
+ {
+ syslog(LOG_ERR, _("Invalid name for myself!"));
+ free(name);
+ return -1;
+ }
+
+ myself->name = name;
+ myself->connection->name = xstrdup(name);
+
+cp
+ if(read_rsa_private_key())
+ return -1;
+
+ if(read_connection_config(myself->connection))
+ {
+ syslog(LOG_ERR, _("Cannot open host configuration file for myself!"));
+ return -1;
+ }
+
+ if(read_rsa_public_key(myself->connection))
+ return -1;
+cp
+
+ if(!get_config_string(lookup_config(myself->connection->config_tree, "Port"), &myport))
+ asprintf(&myport, "655");
+
+/* Read in all the subnets specified in the host configuration file */
+
+ cfg = lookup_config(myself->connection->config_tree, "Subnet");
+
+ while(cfg)
+ {
+ if(!get_config_subnet(cfg, &subnet))
+ return -1;
+
+ subnet_add(myself, subnet);
+
+ cfg = lookup_config_next(myself->connection->config_tree, cfg);
+ }
+
+cp
+ /* Check some options */
+
+ if(get_config_bool(lookup_config(config_tree, "IndirectData"), &choice))
+ if(choice)
+ myself->options |= OPTION_INDIRECT;
+
+ if(get_config_bool(lookup_config(config_tree, "TCPOnly"), &choice))
+ if(choice)
+ myself->options |= OPTION_TCPONLY;
+
+ if(get_config_bool(lookup_config(myself->connection->config_tree, "IndirectData"), &choice))
+ if(choice)
+ myself->options |= OPTION_INDIRECT;
+
+ if(get_config_bool(lookup_config(myself->connection->config_tree, "TCPOnly"), &choice))
+ if(choice)
+ myself->options |= OPTION_TCPONLY;
+
+ if(myself->options & OPTION_TCPONLY)
+ myself->options |= OPTION_INDIRECT;
+
+ if(get_config_string(lookup_config(config_tree, "Mode"), &mode))
+ {
+ if(!strcasecmp(mode, "router"))
+ routing_mode = RMODE_ROUTER;
+ else if (!strcasecmp(mode, "switch"))
+ routing_mode = RMODE_SWITCH;
+ else if (!strcasecmp(mode, "hub"))
+ routing_mode = RMODE_HUB;
+ else
+ {
+ syslog(LOG_ERR, _("Invalid routing mode!"));
+ return -1;
+ }
+ free(mode);
+ }
+ else
+ routing_mode = RMODE_ROUTER;
+
+ get_config_bool(lookup_config(config_tree, "PriorityInheritance"), &priorityinheritance);
+#if !defined(SOL_IP) || !defined(IP_TOS)
+ if(priorityinheritance)
+ syslog(LOG_WARNING, _("PriorityInheritance not supported on this platform"));
+#endif
+
+ if(!get_config_int(lookup_config(config_tree, "MACExpire"), &macexpire))
+ macexpire= 600;
+
+ if(get_config_int(lookup_config(myself->connection->config_tree, "MaxTimeout"), &maxtimeout))
+ {
+ if(maxtimeout <= 0)
+ {
+ syslog(LOG_ERR, _("Bogus maximum timeout!"));
+ return -1;
+ }
+ }
+ else
+ maxtimeout = 900;
+
+ if(get_config_string(lookup_config(config_tree, "AddressFamily"), &afname))
+ {
+ if(!strcasecmp(afname, "IPv4"))
+ addressfamily = AF_INET;
+ else if (!strcasecmp(afname, "IPv6"))
+ addressfamily = AF_INET6;
+ else if (!strcasecmp(afname, "any"))
+ addressfamily = AF_UNSPEC;
+ else
+ {
+ syslog(LOG_ERR, _("Invalid address family!"));
+ return -1;
+ }
+ free(afname);
+ }
+ else
+ addressfamily = AF_INET;
+
+ get_config_bool(lookup_config(config_tree, "Hostnames"), &hostnames);
+cp
+ /* Generate packet encryption key */
+
+ if(get_config_string(lookup_config(myself->connection->config_tree, "Cipher"), &cipher))
+ {
+ if(!strcasecmp(cipher, "none"))
+ {
+ myself->cipher = NULL;
+ }
+ else
+ {
+ if(!(myself->cipher = EVP_get_cipherbyname(cipher)))
+ {
+ syslog(LOG_ERR, _("Unrecognized cipher type!"));
+ return -1;
+ }
+ }
+ }
+ else
+ myself->cipher = EVP_bf_cbc();
+
+ if(myself->cipher)
+ myself->keylength = myself->cipher->key_len + myself->cipher->iv_len;
+ else
+ myself->keylength = 1;
+
+ myself->connection->outcipher = EVP_bf_ofb();
+
+ myself->key = (char *)xmalloc(myself->keylength);
+ RAND_pseudo_bytes(myself->key, myself->keylength);
+
+ if(!get_config_int(lookup_config(config_tree, "KeyExpire"), &keylifetime))
+ keylifetime = 3600;
+
+ keyexpires = now + keylifetime;
+
+ /* Check if we want to use message authentication codes... */
+
+ if(get_config_string(lookup_config(myself->connection->config_tree, "Digest"), &digest))
+ {
+ if(!strcasecmp(digest, "none"))
+ {
+ myself->digest = NULL;
+ }
+ else
+ {
+ if(!(myself->digest = EVP_get_digestbyname(digest)))
+ {
+ syslog(LOG_ERR, _("Unrecognized digest type!"));
+ return -1;
+ }
+ }
+ }
+ else
+ myself->digest = EVP_sha1();
+
+ myself->connection->outdigest = EVP_sha1();
+
+ if(get_config_int(lookup_config(myself->connection->config_tree, "MACLength"), &myself->maclength))
+ {
+ if(myself->digest)
+ {
+ if(myself->maclength > myself->digest->md_size)
+ {
+ syslog(LOG_ERR, _("MAC length exceeds size of digest!"));
+ return -1;
+ }
+ else if (myself->maclength < 0)
+ {
+ syslog(LOG_ERR, _("Bogus MAC length!"));
+ return -1;
+ }
+ }
+ }
+ else
+ myself->maclength = 4;
+
+ myself->connection->outmaclength = 0;
+
+ /* Compression */
+
+ if(get_config_int(lookup_config(myself->connection->config_tree, "Compression"), &myself->compression))
+ {
+ if(myself->compression < 0 || myself->compression > 9)
+ {
+ syslog(LOG_ERR, _("Bogus compression level!"));
+ return -1;
+ }
+ }
+ else
+ myself->compression = 0;
+
+ myself->connection->outcompression = 0;
+cp
+ /* Done */
+
+ myself->nexthop = myself;
+ myself->via = myself;
+ myself->status.active = 1;
+ myself->status.reachable = 1;
+ node_add(myself);
+
+ graph();
+
+cp
+ /* Open sockets */
+
+ memset(&hint, 0, sizeof(hint));
+
+ hint.ai_family = addressfamily;
+ hint.ai_socktype = SOCK_STREAM;
+ hint.ai_protocol = IPPROTO_TCP;
+ hint.ai_flags = AI_PASSIVE;
+
+ if((err = getaddrinfo(NULL, myport, &hint, &ai)) || !ai)
+ {
+ syslog(LOG_ERR, _("System call `%s' failed: %s"), "getaddrinfo", gai_strerror(err));
+ return -1;
+ }
+
+ for(aip = ai; aip; aip = aip->ai_next)
+ {
+ if((listen_socket[listen_sockets].tcp = setup_listen_socket((sockaddr_t *)aip->ai_addr)) < 0)
+ continue;
+
+ if((listen_socket[listen_sockets].udp = setup_vpn_in_socket((sockaddr_t *)aip->ai_addr)) < 0)
+ continue;
+
+ if(debug_lvl >= DEBUG_CONNECTIONS)
+ {
+ hostname = sockaddr2hostname((sockaddr_t *)aip->ai_addr);
+ syslog(LOG_NOTICE, _("Listening on %s"), hostname);
+ free(hostname);
+ }
+
+ listen_socket[listen_sockets].sa.sa = *aip->ai_addr;
+ listen_sockets++;
+ }
+
+ freeaddrinfo(ai);
+
+ if(listen_sockets)
+ syslog(LOG_NOTICE, _("Ready"));
+ else
+ {
+ syslog(LOG_ERR, _("Unable to create any listening socket!"));
+ return -1;
+ }
+cp
+ return 0;
+}
+
+/*
+ setup all initial network connections
+*/
+int setup_network_connections(void)
+{
+cp
+ now = time(NULL);
+
+ init_connections();
+ init_subnets();
+ init_nodes();
+ init_edges();
+ init_events();
+ init_requests();
+
+ if(get_config_int(lookup_config(config_tree, "PingTimeout"), &pingtimeout))
+ {
+ if(pingtimeout < 1)
+ {
+ pingtimeout = 86400;
+ }
+ }
+ else
+ pingtimeout = 60;
+
+ if(setup_device() < 0)
+ return -1;
+
+ /* Run tinc-up script to further initialize the tap interface */
+ execute_script("tinc-up");
+
+ if(setup_myself() < 0)
+ return -1;
+
+ try_outgoing_connections();
+cp
+ return 0;
+}
+
+/*
+ close all open network connections
+*/
+void close_network_connections(void)
+{
+ avl_node_t *node, *next;
+ connection_t *c;
+ int i;
+cp
+ for(node = connection_tree->head; node; node = next)
+ {
+ next = node->next;
+ c = (connection_t *)node->data;
+ if(c->outgoing)
+ free(c->outgoing->name), free(c->outgoing), c->outgoing = NULL;
+ terminate_connection(c, 0);
+ }
+
+ if(myself && myself->connection)
+ terminate_connection(myself->connection, 0);
+
+ for(i = 0; i < listen_sockets; i++)
+ {
+ close(listen_socket[i].tcp);
+ close(listen_socket[i].udp);
+ }
+
+ exit_requests();
+ exit_events();
+ exit_edges();
+ exit_subnets();
+ exit_nodes();
+ exit_connections();
+
+ execute_script("tinc-down");
+
+ close_device();
+cp
+ return;
+}
--- /dev/null
+/*
+ net_socket.c -- Handle various kinds of sockets.
+ Copyright (C) 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
+ 2000-2002 Guus Sliepen <guus@sliepen.warande.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: net_socket.c,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+#include "config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#ifdef HAVE_LINUX
+ #include <netinet/ip.h>
+ #include <netinet/tcp.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+/* SunOS really wants sys/socket.h BEFORE net/if.h,
+ and FreeBSD wants these lines below the rest. */
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+#include <utils.h>
+#include <xalloc.h>
+#include <avl_tree.h>
+#include <list.h>
+
+#include "conf.h"
+#include "connection.h"
+#include "meta.h"
+#include "net.h"
+#include "netutl.h"
+#include "process.h"
+#include "protocol.h"
+#include "subnet.h"
+#include "graph.h"
+#include "process.h"
+#include "route.h"
+#include "device.h"
+#include "event.h"
+
+#include "system.h"
+
+int addressfamily = AF_INET;
+int maxtimeout = 900;
+int seconds_till_retry = 5;
+
+listen_socket_t listen_socket[MAXSOCKETS];
+int listen_sockets = 0;
+
+/* Setup sockets */
+
+int setup_listen_socket(sockaddr_t *sa)
+{
+ int nfd, flags;
+ char *addrstr;
+ int option;
+#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
+ char *interface;
+ struct ifreq ifr;
+#endif
+cp
+ if((nfd = socket(sa->sa.sa_family, SOCK_STREAM, IPPROTO_TCP)) < 0)
+ {
+ syslog(LOG_ERR, _("Creating metasocket failed: %s"), strerror(errno));
+ return -1;
+ }
+
+ flags = fcntl(nfd, F_GETFL);
+ if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0)
+ {
+ close(nfd);
+ syslog(LOG_ERR, _("System call `%s' failed: %s"), "fcntl", strerror(errno));
+ return -1;
+ }
+
+ /* Optimize TCP settings */
+
+ option = 1;
+ setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
+
+#if defined(SOL_TCP) && defined(TCP_NODELAY)
+ setsockopt(nfd, SOL_TCP, TCP_NODELAY, &option, sizeof(option));
+#endif
+
+#if defined(SOL_IP) && defined(IP_TOS) && defined(IPTOS_LOWDELAY)
+ option = IPTOS_LOWDELAY;
+ setsockopt(nfd, SOL_IP, IP_TOS, &option, sizeof(option));
+#endif
+
+ if(get_config_string(lookup_config(config_tree, "BindToInterface"), &interface))
+ {
+#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_ifrn.ifrn_name, interface, IFNAMSIZ);
+ if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)))
+ {
+ close(nfd);
+ syslog(LOG_ERR, _("Can't bind to interface %s: %s"), interface, strerror(errno));
+ return -1;
+ }
+#else
+ syslog(LOG_WARNING, _("BindToDevice not supported on this platform"));
+#endif
+ }
+
+ if(bind(nfd, &sa->sa, SALEN(sa->sa)))
+ {
+ close(nfd);
+ addrstr = sockaddr2hostname(sa);
+ syslog(LOG_ERR, _("Can't bind to %s/tcp: %s"), addrstr, strerror(errno));
+ free(addrstr);
+ return -1;
+ }
+
+ if(listen(nfd, 3))
+ {
+ close(nfd);
+ syslog(LOG_ERR, _("System call `%s' failed: %s"), "listen", strerror(errno));
+ return -1;
+ }
+cp
+ return nfd;
+}
+
+int setup_vpn_in_socket(sockaddr_t *sa)
+{
+ int nfd, flags;
+ char *addrstr;
+ int option;
+#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
+ char *interface;
+ struct ifreq ifr;
+#endif
+cp
+ if((nfd = socket(sa->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+ {
+ syslog(LOG_ERR, _("Creating UDP socket failed: %s"), strerror(errno));
+ return -1;
+ }
+
+ flags = fcntl(nfd, F_GETFL);
+ if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0)
+ {
+ close(nfd);
+ syslog(LOG_ERR, _("System call `%s' failed: %s"), "fcntl", strerror(errno));
+ return -1;
+ }
+
+ option = 1;
+ setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
+
+#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
+ if(get_config_string(lookup_config(config_tree, "BindToInterface"), &interface))
+ {
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_ifrn.ifrn_name, interface, IFNAMSIZ);
+ if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)))
+ {
+ close(nfd);
+ syslog(LOG_ERR, _("Can't bind to interface %s: %s"), interface, strerror(errno));
+ return -1;
+ }
+ }
+#endif
+
+ if(bind(nfd, &sa->sa, SALEN(sa->sa)))
+ {
+ close(nfd);
+ addrstr = sockaddr2hostname(sa);
+ syslog(LOG_ERR, _("Can't bind to %s/udp: %s"), addrstr, strerror(errno));
+ free(addrstr);
+ return -1;
+ }
+cp
+ return nfd;
+}
+
+void retry_outgoing(outgoing_t *outgoing)
+{
+ event_t *event;
+cp
+ outgoing->timeout += 5;
+ if(outgoing->timeout > maxtimeout)
+ outgoing->timeout = maxtimeout;
+
+ event = new_event();
+ event->handler = (event_handler_t)setup_outgoing_connection;
+ event->time = now + outgoing->timeout;
+ event->data = outgoing;
+ event_add(event);
+
+ if(debug_lvl >= DEBUG_CONNECTIONS)
+ syslog(LOG_NOTICE, _("Trying to re-establish outgoing connection in %d seconds"), outgoing->timeout);
+cp
+}
+
+int setup_outgoing_socket(connection_t *c)
+{
+ int option;
+cp
+ if(debug_lvl >= DEBUG_CONNECTIONS)
+ syslog(LOG_INFO, _("Trying to connect to %s (%s)"), c->name, c->hostname);
+
+ c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
+
+ if(c->socket == -1)
+ {
+ syslog(LOG_ERR, _("Creating socket for %s failed: %s"), c->hostname, strerror(errno));
+ return -1;
+ }
+
+ /* Optimize TCP settings */
+
+#ifdef HAVE_LINUX
+ option = 1;
+ setsockopt(c->socket, SOL_TCP, TCP_NODELAY, &option, sizeof(option));
+
+ option = IPTOS_LOWDELAY;
+ setsockopt(c->socket, SOL_IP, IP_TOS, &option, sizeof(option));
+#endif
+
+ /* Connect */
+
+ if(connect(c->socket, &c->address.sa, SALEN(c->address.sa)) == -1)
+ {
+ close(c->socket);
+ syslog(LOG_ERR, _("Error while connecting to %s (%s): %s"), c->name, c->hostname, strerror(errno));
+ return -1;
+ }
+
+ if(debug_lvl >= DEBUG_CONNECTIONS)
+ syslog(LOG_INFO, _("Connected to %s (%s)"), c->name, c->hostname);
+cp
+ return 0;
+}
+
+
+void finish_connecting(connection_t *c)
+{
+cp
+ if(debug_lvl >= DEBUG_CONNECTIONS)
+ syslog(LOG_INFO, _("Connected to %s (%s)"), c->name, c->hostname);
+
+ c->last_ping_time = now;
+
+ send_id(c);
+cp
+}
+
+void do_outgoing_connection(connection_t *c)
+{
+ char *address, *port;
+ int option, result, flags;
+cp
+begin:
+ if(!c->outgoing->ai)
+ {
+ if(!c->outgoing->cfg)
+ {
+ if(debug_lvl >= DEBUG_CONNECTIONS)
+ syslog(LOG_ERR, _("Could not set up a meta connection to %s"), c->name);
+ c->status.remove = 1;
+ retry_outgoing(c->outgoing);
+ return;
+ }
+
+ get_config_string(c->outgoing->cfg, &address);
+
+ if(!get_config_string(lookup_config(c->config_tree, "Port"), &port))
+ asprintf(&port, "655");
+
+ c->outgoing->ai = str2addrinfo(address, port, SOCK_STREAM);
+ free(address);
+ free(port);
+
+ c->outgoing->aip = c->outgoing->ai;
+ c->outgoing->cfg = lookup_config_next(c->config_tree, c->outgoing->cfg);
+ }
+
+ if(!c->outgoing->aip)
+ {
+ freeaddrinfo(c->outgoing->ai);
+ c->outgoing->ai = NULL;
+ goto begin;
+ }
+
+ memcpy(&c->address, c->outgoing->aip->ai_addr, c->outgoing->aip->ai_addrlen);
+ c->outgoing->aip = c->outgoing->aip->ai_next;
+
+ if(c->hostname)
+ free(c->hostname);
+
+ c->hostname = sockaddr2hostname(&c->address);
+
+ if(debug_lvl >= DEBUG_CONNECTIONS)
+ syslog(LOG_INFO, _("Trying to connect to %s (%s)"), c->name, c->hostname);
+
+ c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
+
+ if(c->socket == -1)
+ {
+ if(debug_lvl >= DEBUG_CONNECTIONS)
+ syslog(LOG_ERR, _("Creating socket for %s failed: %s"), c->hostname, strerror(errno));
+
+ goto begin;
+ }
+
+ /* Optimize TCP settings */
+
+#ifdef HAVE_LINUX
+ option = 1;
+ setsockopt(c->socket, SOL_TCP, TCP_NODELAY, &option, sizeof(option));
+
+ option = IPTOS_LOWDELAY;
+ setsockopt(c->socket, SOL_IP, IP_TOS, &option, sizeof(option));
+#endif
+
+ /* Non-blocking */
+
+ flags = fcntl(c->socket, F_GETFL);
+
+ if(fcntl(c->socket, F_SETFL, flags | O_NONBLOCK) < 0)
+ {
+ syslog(LOG_ERR, _("fcntl for %s: %s"), c->hostname, strerror(errno));
+ }
+
+ /* Connect */
+
+ result = connect(c->socket, &c->address.sa, SALEN(c->address.sa));
+
+ if(result == -1)
+ {
+ if(errno == EINPROGRESS)
+ {
+ c->status.connecting = 1;
+ return;
+ }
+
+ close(c->socket);
+
+ if(debug_lvl >= DEBUG_CONNECTIONS)
+ syslog(LOG_ERR, _("%s: %s"), c->hostname, strerror(errno));
+
+ goto begin;
+ }
+
+ finish_connecting(c);
+ return;
+cp
+}
+
+void setup_outgoing_connection(outgoing_t *outgoing)
+{
+ connection_t *c;
+ node_t *n;
+cp
+ n = lookup_node(outgoing->name);
+
+ if(n)
+ if(n->connection)
+ {
+ if(debug_lvl >= DEBUG_CONNECTIONS)
+ syslog(LOG_INFO, _("Already connected to %s"), outgoing->name);
+ n->connection->outgoing = outgoing;
+ return;
+ }
+
+ c = new_connection();
+ c->name = xstrdup(outgoing->name);
+ c->outcipher = myself->connection->outcipher;
+ c->outdigest = myself->connection->outdigest;
+ c->outmaclength = myself->connection->outmaclength;
+ c->outcompression = myself->connection->outcompression;
+
+ init_configuration(&c->config_tree);
+ read_connection_config(c);
+
+ outgoing->cfg = lookup_config(c->config_tree, "Address");
+
+ if(!outgoing->cfg)
+ {
+ syslog(LOG_ERR, _("No address specified for %s"), c->name);
+ free_connection(c);
+ free(outgoing->name);
+ free(outgoing);
+ return;
+ }
+
+ c->outgoing = outgoing;
+ c->last_ping_time = now;
+
+ connection_add(c);
+
+ do_outgoing_connection(c);
+}
+
+/*
+ accept a new tcp connect and create a
+ new connection
+*/
+int handle_new_meta_connection(int sock)
+{
+ connection_t *c;
+ sockaddr_t sa;
+ int fd, len = sizeof(sa);
+cp
+ if((fd = accept(sock, &sa.sa, &len)) < 0)
+ {
+ syslog(LOG_ERR, _("Accepting a new connection failed: %s"), strerror(errno));
+ return -1;
+ }
+
+ sockaddrunmap(&sa);
+
+ c = new_connection();
+ c->outcipher = myself->connection->outcipher;
+ c->outdigest = myself->connection->outdigest;
+ c->outmaclength = myself->connection->outmaclength;
+ c->outcompression = myself->connection->outcompression;
+
+ c->address = sa;
+ c->hostname = sockaddr2hostname(&sa);
+ c->socket = fd;
+ c->last_ping_time = now;
+
+ if(debug_lvl >= DEBUG_CONNECTIONS)
+ syslog(LOG_NOTICE, _("Connection from %s"), c->hostname);
+
+ connection_add(c);
+
+ c->allow_request = ID;
+ send_id(c);
+cp
+ return 0;
+}
+
+void try_outgoing_connections(void)
+{
+ static config_t *cfg = NULL;
+ char *name;
+ outgoing_t *outgoing;
+cp
+ for(cfg = lookup_config(config_tree, "ConnectTo"); cfg; cfg = lookup_config_next(config_tree, cfg))
+ {
+ get_config_string(cfg, &name);
+
+ if(check_id(name))
+ {
+ syslog(LOG_ERR, _("Invalid name for outgoing connection in %s line %d"), cfg->file, cfg->line);
+ free(name);
+ continue;
+ }
+
+ outgoing = xmalloc_and_zero(sizeof(*outgoing));
+ outgoing->name = name;
+ setup_outgoing_connection(outgoing);
+ }
+}
/*
netutl.c -- some supporting network utility code
- Copyright (C) 1998,1999,2000 Ivo Timmermans <itimmermans@bigfoot.com>
+ Copyright (C) 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>
+ 2000-2002 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- $Id: netutl.c,v 1.13 2000/10/18 20:12:09 zarq Exp $
+ $Id: netutl.c,v 1.14 2002/04/09 15:26:00 zarq Exp $
*/
#include "config.h"
-#include <arpa/inet.h>
+#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <signal.h>
#include <sys/socket.h>
#include <syslog.h>
+#include <arpa/inet.h>
#include <utils.h>
#include <xalloc.h>
#include "errno.h"
#include "conf.h"
-#include "encr.h"
#include "net.h"
#include "netutl.h"
#include "system.h"
+int hostnames = 0;
/*
- free a queue and all of its elements
+ Turn a string into a struct addrinfo.
+ Return NULL on failure.
*/
-void destroy_queue(packet_queue_t *pq)
+struct addrinfo *str2addrinfo(char *address, char *service, int socktype)
{
- queue_element_t *p, *q;
+ struct addrinfo hint, *ai;
+ int err;
cp
- for(p = pq->head; p != NULL; p = q)
+ memset(&hint, 0, sizeof(hint));
+
+ hint.ai_family = addressfamily;
+ hint.ai_socktype = socktype;
+
+ if((err = getaddrinfo(address, service, &hint, &ai)))
{
- q = p->next;
- if(p->packet)
- free(p->packet);
- free(p);
+ if(debug_lvl >= DEBUG_ERROR)
+ syslog(LOG_WARNING, _("Error looking up %s port %s: %s\n"), address, service, gai_strerror(err));
+ cp_trace();
+ return NULL;
}
- free(pq);
cp
+ return ai;
}
-
-char *hostlookup(unsigned long addr)
+sockaddr_t str2sockaddr(char *address, char *port)
{
- char *name;
- struct hostent *host = NULL;
- struct in_addr in;
- config_t const *cfg;
- int lookup_hostname;
+ struct addrinfo hint, *ai;
+ sockaddr_t result;
+ int err;
cp
- in.s_addr = addr;
+ memset(&hint, 0, sizeof(hint));
- lookup_hostname = 0;
- if((cfg = get_config_val(config, resolve_dns)) != NULL)
- if(cfg->data.val == stupid_true)
- lookup_hostname = 1;
+ hint.ai_family = AF_UNSPEC;
+ hint.ai_flags = AI_NUMERICHOST;
+ hint.ai_socktype = SOCK_STREAM;
- if(lookup_hostname)
- host = gethostbyaddr((char *)&in, sizeof(in), AF_INET);
-
- if(!lookup_hostname || !host)
+ if((err = getaddrinfo(address, port, &hint, &ai) || !ai))
{
- asprintf(&name, "%s", inet_ntoa(in));
+ syslog(LOG_ERR, _("Error looking up %s port %s: %s\n"), address, port, gai_strerror(err));
+ cp_trace();
+ raise(SIGFPE);
+ exit(0);
}
- else
+
+ result = *(sockaddr_t *)ai->ai_addr;
+ freeaddrinfo(ai);
+cp
+ return result;
+}
+
+void sockaddr2str(sockaddr_t *sa, char **addrstr, char **portstr)
+{
+ char address[NI_MAXHOST];
+ char port[NI_MAXSERV];
+ char *scopeid;
+ int err;
+cp
+ if((err = getnameinfo(&sa->sa, SALEN(sa->sa), address, sizeof(address), port, sizeof(port), NI_NUMERICHOST|NI_NUMERICSERV)))
{
- asprintf(&name, "%s", host->h_name);
+ syslog(LOG_ERR, _("Error while translating addresses: %s"), gai_strerror(err));
+ cp_trace();
+ raise(SIGFPE);
+ exit(0);
}
+
+#ifdef HAVE_LINUX
+ if((scopeid = strchr(address, '%')))
+ *scopeid = '\0'; /* Descope. */
+#endif
+
+ *addrstr = xstrdup(address);
+ *portstr = xstrdup(port);
cp
- return name;
}
-/*
- Turn a string into an IP addy with netmask
- return NULL on failure
-*/
-ip_mask_t *strtoip(char *str)
+char *sockaddr2hostname(sockaddr_t *sa)
{
- ip_mask_t *ip;
- int masker;
- char *q, *p;
- struct hostent *h;
+ char *str;
+ char address[NI_MAXHOST] = "unknown";
+ char port[NI_MAXSERV] = "unknown";
+ int err;
cp
- p = str;
- if((q = strchr(p, '/')))
+ if((err = getnameinfo(&sa->sa, SALEN(sa->sa), address, sizeof(address), port, sizeof(port), hostnames?0:(NI_NUMERICHOST|NI_NUMERICSERV))))
{
- *q = '\0';
- q++; /* q now points to netmask part, or NULL if no mask */
+ syslog(LOG_ERR, _("Error while looking up hostname: %s"), gai_strerror(err));
}
- if(!(h = gethostbyname(p)))
+ asprintf(&str, _("%s port %s"), address, port);
+cp
+ return str;
+}
+
+int sockaddrcmp(sockaddr_t *a, sockaddr_t *b)
+{
+ int result;
+cp
+ result = a->sa.sa_family - b->sa.sa_family;
+
+ if(result)
+ return result;
+
+ switch(a->sa.sa_family)
{
- fprintf(stderr, _("Error looking up `%s': %s\n"), p, strerror(errno));
- return NULL;
+ case AF_UNSPEC:
+ return 0;
+ case AF_INET:
+ result = memcmp(&a->in.sin_addr, &b->in.sin_addr, sizeof(a->in.sin_addr));
+ if(result)
+ return result;
+ return memcmp(&a->in.sin_port, &b->in.sin_port, sizeof(a->in.sin_port));
+ case AF_INET6:
+ result = memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr));
+ if(result)
+ return result;
+ return memcmp(&a->in6.sin6_port, &b->in6.sin6_port, sizeof(a->in6.sin6_port));
+ default:
+ syslog(LOG_ERR, _("sockaddrcmp() was called with unknown address family %d, exitting!"), a->sa.sa_family);
+ cp_trace();
+ raise(SIGFPE);
+ exit(0);
}
+cp
+}
- masker = 0;
- if(q)
+void sockaddrunmap(sockaddr_t *sa)
+{
+ if(sa->sa.sa_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&sa->in6.sin6_addr))
{
- masker = strtol(q, &p, 10);
- if(q == p || (*p))
- return NULL;
+ sa->in.sin_addr.s_addr = ((uint32_t *)&sa->in6.sin6_addr)[3];
+ sa->in.sin_family = AF_INET;
}
+}
+
+/* Subnet mask handling */
+
+int maskcmp(char *a, char *b, int masklen, int len)
+{
+ int i, m, result;
+cp
+ for(m = masklen, i = 0; m >= 8; m -= 8, i++)
+ if((result = a[i] - b[i]))
+ return result;
+
+ if(m)
+ return (a[i] & (0x100 - (1 << (8 - m)))) - (b[i] & (0x100 - (1 << (8 - m))));
+
+ return 0;
+}
- ip = xmalloc(sizeof(*ip));
- ip->ip = ntohl(*((ip_t*)(h->h_addr_list[0])));
+void mask(char *a, int masklen, int len)
+{
+ int i;
+cp
+ i = masklen / 8;
+ masklen %= 8;
+
+ if(masklen)
+ a[i++] &= (0x100 - (1 << masklen));
+
+ for(; i < len; i++)
+ a[i] = 0;
+}
- ip->mask = masker ? ~((1 << (32 - masker)) - 1) : 0;
+void maskcpy(char *a, char *b, int masklen, int len)
+{
+ int i, m;
cp
- return ip;
+ for(m = masklen, i = 0; m >= 8; m -= 8, i++)
+ a[i] = b[i];
+
+ if(m)
+ {
+ a[i] = b[i] & (0x100 - (1 << m));
+ i++;
+ }
+
+ for(; i < len; i++)
+ a[i] = 0;
}
+int maskcheck(char *a, int masklen, int len)
+{
+ int i;
+cp
+ i = masklen / 8;
+ masklen %= 8;
+
+ if(masklen)
+ if(a[i++] & (char)~(0x100 - (1 << masklen)))
+ return -1;
+
+ for(; i < len; i++)
+ if(a[i] != 0)
+ return -1;
+
+ return 0;
+}
/*
netutl.h -- header file for netutl.c
- Copyright (C) 1998,1999,2000 Ivo Timmermans <zarq@iname.com>
+ Copyright (C) 1998-2002 Ivo Timmermans <zarq@iname.com>
+ 2000-2002 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- $Id: netutl.h,v 1.3 2000/10/18 20:12:09 zarq Exp $
+ $Id: netutl.h,v 1.4 2002/04/09 15:26:00 zarq Exp $
*/
#ifndef __TINC_NETUTL_H__
#define __TINC_NETUTL_H__
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
#include "net.h"
-#include "conf.h"
+
+extern int hostnames;
extern char *hostlookup(unsigned long);
-extern ip_mask_t *strtoip(char*);
+extern struct addrinfo *str2addrinfo(char *, char *, int);
+extern sockaddr_t str2sockaddr(char *, char *);
+extern void sockaddr2str(sockaddr_t *, char **, char **);
+extern char *sockaddr2hostname(sockaddr_t *);
+extern int sockaddrcmp(sockaddr_t *, sockaddr_t *);
+extern void sockaddrunmap(sockaddr_t *);
+extern int maskcmp(char *, char *, int, int);
+extern void maskcpy(char *, char *, int, int);
+extern void mask(char *, int, int);
+extern int maskcheck(char *, int, int);
#endif /* __TINC_NETUTL_H__ */
--- /dev/null
+/*
+ node.c -- node tree management
+ Copyright (C) 2001-2002 Guus Sliepen <guus@sliepen.warande.net>,
+ 2001-2002 Ivo Timmermans <itimmermans@bigfoot.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: node.c,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+#include "config.h"
+
+#include <string.h>
+#include <syslog.h>
+
+#include <avl_tree.h>
+#include "node.h"
+#include "netutl.h"
+#include "net.h"
+#include <utils.h>
+#include <xalloc.h>
+
+#include "system.h"
+
+avl_tree_t *node_tree; /* Known nodes, sorted by name */
+avl_tree_t *node_udp_tree; /* Known nodes, sorted by address and port */
+
+node_t *myself;
+
+int node_compare(node_t *a, node_t *b)
+{
+ return strcmp(a->name, b->name);
+}
+
+int node_udp_compare(node_t *a, node_t *b)
+{
+ int result;
+cp
+ result = sockaddrcmp(&a->address, &b->address);
+
+ if(result)
+ return result;
+
+ return (a->name && b->name)?strcmp(a->name, b->name):0;
+}
+
+void init_nodes(void)
+{
+cp
+ node_tree = avl_alloc_tree((avl_compare_t)node_compare, NULL);
+ node_udp_tree = avl_alloc_tree((avl_compare_t)node_udp_compare, NULL);
+cp
+}
+
+void exit_nodes(void)
+{
+cp
+ avl_delete_tree(node_tree);
+ avl_delete_tree(node_udp_tree);
+cp
+}
+
+node_t *new_node(void)
+{
+ node_t *n = (node_t *)xmalloc_and_zero(sizeof(*n));
+cp
+ n->subnet_tree = new_subnet_tree();
+ n->edge_tree = new_edge_tree();
+ n->queue = list_alloc((list_action_t)free);
+cp
+ return n;
+}
+
+void free_node(node_t *n)
+{
+cp
+ if(n->queue)
+ list_delete_list(n->queue);
+ if(n->name)
+ free(n->name);
+ if(n->hostname)
+ free(n->hostname);
+ if(n->key)
+ free(n->key);
+ if(n->subnet_tree)
+ free_subnet_tree(n->subnet_tree);
+ if(n->edge_tree)
+ free_edge_tree(n->edge_tree);
+ free(n);
+cp
+}
+
+void node_add(node_t *n)
+{
+cp
+ avl_insert(node_tree, n);
+ avl_insert(node_udp_tree, n);
+cp
+}
+
+void node_del(node_t *n)
+{
+ avl_node_t *node, *next;
+ edge_t *e;
+ subnet_t *s;
+cp
+ for(node = n->subnet_tree->head; node; node = next)
+ {
+ next = node->next;
+ s = (subnet_t *)node->data;
+ subnet_del(n, s);
+ }
+
+ for(node = n->subnet_tree->head; node; node = next)
+ {
+ next = node->next;
+ e = (edge_t *)node->data;
+ edge_del(e);
+ }
+cp
+ avl_delete(node_tree, n);
+ avl_delete(node_udp_tree, n);
+cp
+}
+
+node_t *lookup_node(char *name)
+{
+ node_t n;
+cp
+ n.name = name;
+ return avl_search(node_tree, &n);
+}
+
+node_t *lookup_node_udp(sockaddr_t *sa)
+{
+ node_t n;
+cp
+ n.address = *sa;
+ n.name = NULL;
+
+ return avl_search(node_udp_tree, &n);
+}
+
+void dump_nodes(void)
+{
+ avl_node_t *node;
+ node_t *n;
+cp
+ syslog(LOG_DEBUG, _("Nodes:"));
+
+ for(node = node_tree->head; node; node = node->next)
+ {
+ n = (node_t *)node->data;
+ syslog(LOG_DEBUG, _(" %s at %s cipher %d digest %d maclength %d compression %d options %lx status %04x nexthop %s via %s"),
+ n->name, n->hostname, n->cipher?n->cipher->nid:0, n->digest?n->digest->type:0, n->maclength, n->compression, n->options,
+ n->status, n->nexthop?n->nexthop->name:"-", n->via?n->via->name:"-");
+ }
+
+ syslog(LOG_DEBUG, _("End of nodes."));
+cp
+}
--- /dev/null
+/*
+ node.h -- header for node.c
+ Copyright (C) 2001-2002 Guus Sliepen <guus@sliepen.warande.net>,
+ 2001-2002 Ivo Timmermans <itimmermans@bigfoot.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: node.h,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+#ifndef __TINC_NODE_H__
+#define __TINC_NODE_H__
+
+#include <avl_tree.h>
+
+#include "subnet.h"
+#include "connection.h"
+
+typedef struct node_status_t {
+ int active:1; /* 1 if active.. */
+ int validkey:1; /* 1 if we currently have a valid key for him */
+ int waitingforkey:1; /* 1 if we already sent out a request */
+ int visited:1; /* 1 if this node has been visited by one of the graph algorithms */
+ int reachable:1; /* 1 if this node is reachable in the graph */
+ int indirect:1; /* 1 if this node is not directly reachable by us */
+ int unused:26;
+} node_status_t;
+
+typedef struct node_t {
+ char *name; /* name of this node */
+ long int options; /* options turned on for this node */
+
+ sockaddr_t address; /* his real (internet) ip to send UDP packets to */
+ char *hostname; /* the hostname of its real ip */
+
+ struct node_status_t status;
+
+ const EVP_CIPHER *cipher; /* Cipher type for UDP packets */
+ char *key; /* Cipher key and iv */
+ int keylength; /* Cipher key and iv length*/
+
+ const EVP_MD *digest; /* Digest type for MAC */
+ int maclength; /* Length of MAC */
+
+ int compression; /* Compressionlevel, 0 = no compression */
+
+ list_t *queue; /* Queue for packets awaiting to be encrypted */
+
+ struct node_t *nexthop; /* nearest node from us to him */
+ struct node_t *via; /* next hop for UDP packets */
+
+ avl_tree_t *subnet_tree; /* Pointer to a tree of subnets belonging to this node */
+
+ avl_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) */
+
+ unsigned int sent_seqno; /* Sequence number last sent to this node */
+ unsigned int received_seqno; /* Sequence number last received from this node */
+} node_t;
+
+extern struct node_t *myself;
+extern avl_tree_t *node_tree;
+extern avl_tree_t *node_udp_tree;
+
+extern void init_nodes(void);
+extern void exit_nodes(void);
+extern node_t *new_node(void);
+extern void free_node(node_t *);
+extern void node_add(node_t *);
+extern void node_del(node_t *);
+extern node_t *lookup_node(char *);
+extern node_t *lookup_node_udp(sockaddr_t *);
+extern void dump_nodes(void);
+
+#endif /* __TINC_NODE_H__ */
--- /dev/null
+/*
+ process.c -- process management functions
+ Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
+ 2000-2002 Guus Sliepen <guus@sliepen.warande.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: process.c,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+#include "config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <termios.h>
+
+#include <pidfile.h>
+#include <utils.h>
+#include <xalloc.h>
+
+#include "conf.h"
+#include "process.h"
+#include "subnet.h"
+#include "device.h"
+#include "connection.h"
+#include "device.h"
+
+#include "system.h"
+
+/* If zero, don't detach from the terminal. */
+int do_detach = 1;
+
+extern char *identname;
+extern char *pidfilename;
+extern char **g_argv;
+
+sigset_t emptysigset;
+
+static int saved_debug_lvl = 0;
+
+extern int sighup;
+extern int sigalrm;
+extern int do_purge;
+
+void memory_full(int size)
+{
+ syslog(LOG_ERR, _("Memory exhausted (couldn't allocate %d bytes), exitting."), size);
+ cp_trace();
+ exit(1);
+}
+
+/* Some functions the less gifted operating systems might lack... */
+
+#ifndef HAVE_FCLOSEALL
+int fcloseall(void)
+{
+ fflush(stdin);
+ fflush(stdout);
+ fflush(stderr);
+ fclose(stdin);
+ fclose(stdout);
+ fclose(stderr);
+ return 0;
+}
+#endif
+
+/*
+ Close network connections, and terminate neatly
+*/
+void cleanup_and_exit(int c)
+{
+cp
+ close_network_connections();
+
+ if(debug_lvl > DEBUG_NOTHING)
+ dump_device_stats();
+
+ syslog(LOG_NOTICE, _("Terminating"));
+
+ closelog();
+ exit(c);
+}
+
+/*
+ check for an existing tinc for this net, and write pid to pidfile
+*/
+int write_pidfile(void)
+{
+ int pid;
+cp
+ if((pid = check_pid(pidfilename)))
+ {
+ if(netname)
+ fprintf(stderr, _("A tincd is already running for net `%s' with pid %d.\n"),
+ netname, pid);
+ else
+ fprintf(stderr, _("A tincd is already running with pid %d.\n"), pid);
+ return 1;
+ }
+
+ /* if it's locked, write-protected, or whatever */
+ if(!write_pid(pidfilename))
+ return 1;
+cp
+ return 0;
+}
+
+/*
+ kill older tincd for this net
+*/
+int kill_other(int signal)
+{
+ int pid;
+cp
+ if(!(pid = read_pid(pidfilename)))
+ {
+ if(netname)
+ fprintf(stderr, _("No other tincd is running for net `%s'.\n"), netname);
+ else
+ fprintf(stderr, _("No other tincd is running.\n"));
+ return 1;
+ }
+
+ errno = 0; /* No error, sometimes errno is only changed on error */
+ /* ESRCH is returned when no process with that pid is found */
+ if(kill(pid, signal) && errno == ESRCH)
+ {
+ if(netname)
+ fprintf(stderr, _("The tincd for net `%s' is no longer running. "), netname);
+ else
+ fprintf(stderr, _("The tincd is no longer running. "));
+
+ fprintf(stderr, _("Removing stale lock file.\n"));
+ remove_pid(pidfilename);
+ }
+cp
+ return 0;
+}
+
+/*
+ Detach from current terminal, write pidfile, kill parent
+*/
+int detach(void)
+{
+cp
+ setup_signals();
+
+ /* First check if we can open a fresh new pidfile */
+
+ if(write_pidfile())
+ return -1;
+
+ /* If we succeeded in doing that, detach */
+
+ closelog();
+
+ if(do_detach)
+ {
+ if(daemon(0, 0) < 0)
+ {
+ fprintf(stderr, _("Couldn't detach from terminal: %s"), strerror(errno));
+ return -1;
+ }
+
+ /* Now UPDATE the pid in the pidfile, because we changed it... */
+
+ if(!write_pid(pidfilename))
+ return -1;
+ }
+
+ openlog(identname, LOG_CONS | LOG_PID, LOG_DAEMON);
+
+ if(debug_lvl > DEBUG_NOTHING)
+ syslog(LOG_NOTICE, _("tincd %s (%s %s) starting, debug level %d"),
+ VERSION, __DATE__, __TIME__, debug_lvl);
+ else
+ syslog(LOG_NOTICE, _("tincd %s starting"), VERSION);
+
+ xalloc_fail_func = memory_full;
+cp
+ return 0;
+}
+
+/*
+ Execute the program name, with sane environment. All output will be
+ redirected to syslog.
+*/
+void _execute_script(const char *scriptname) __attribute__ ((noreturn));
+void _execute_script(const char *scriptname)
+{
+ char *s;
+cp
+#ifdef HAVE_UNSETENV
+ unsetenv("NETNAME");
+ unsetenv("DEVICE");
+ unsetenv("INTERFACE");
+#endif
+
+ if(netname)
+ {
+ asprintf(&s, "NETNAME=%s", netname);
+ putenv(s); /* Don't free s! see man 3 putenv */
+ }
+
+ if(device)
+ {
+ asprintf(&s, "DEVICE=%s", device);
+ putenv(s); /* Don't free s! see man 3 putenv */
+ }
+
+ if(interface)
+ {
+ asprintf(&s, "INTERFACE=%s", interface);
+ putenv(s); /* Don't free s! see man 3 putenv */
+ }
+
+ chdir("/");
+
+ /* Close all file descriptors */
+ closelog(); /* <- this means we cannot use syslog() here anymore! */
+ fcloseall();
+
+ execl(scriptname, NULL);
+ /* No return on success */
+
+ if(errno != ENOENT) /* Ignore if the file does not exist */
+ exit(1); /* Some error while trying execl(). */
+ else
+ exit(0);
+}
+
+/*
+ Fork and execute the program pointed to by name.
+*/
+int execute_script(const char *name)
+{
+ pid_t pid;
+ int status;
+ struct stat s;
+ char *scriptname;
+cp
+ asprintf(&scriptname, "%s/%s", confbase, name);
+
+ /* First check if there is a script */
+
+ if(stat(scriptname, &s))
+ return 0;
+
+ if((pid = fork()) < 0)
+ {
+ syslog(LOG_ERR, _("System call `%s' failed: %s"), "fork", strerror(errno));
+ return -1;
+ }
+
+ if(pid)
+ {
+ if(debug_lvl >= DEBUG_STATUS)
+ syslog(LOG_INFO, _("Executing script %s"), name);
+
+ free(scriptname);
+
+ if(waitpid(pid, &status, 0) == pid)
+ {
+ if(WIFEXITED(status)) /* Child exited by itself */
+ {
+ if(WEXITSTATUS(status))
+ {
+ syslog(LOG_ERR, _("Process %d (%s) exited with non-zero status %d"), pid, name, WEXITSTATUS(status));
+ return -1;
+ }
+ else
+ return 0;
+ }
+ else if(WIFSIGNALED(status)) /* Child was killed by a signal */
+ {
+ syslog(LOG_ERR, _("Process %d (%s) was killed by signal %d (%s)"),
+ pid, name, WTERMSIG(status), strsignal(WTERMSIG(status)));
+ return -1;
+ }
+ else /* Something strange happened */
+ {
+ syslog(LOG_ERR, _("Process %d (%s) terminated abnormally"), pid, name);
+ return -1;
+ }
+ }
+ else
+ {
+ syslog(LOG_ERR, _("System call `%s' failed: %s"), "waitpid", strerror(errno));
+ return -1;
+ }
+ }
+cp
+ /* Child here */
+
+ _execute_script(scriptname);
+}
+
+
+/*
+ Signal handlers.
+*/
+
+RETSIGTYPE
+sigterm_handler(int a)
+{
+ if(debug_lvl > DEBUG_NOTHING)
+ syslog(LOG_NOTICE, _("Got TERM signal"));
+
+ cleanup_and_exit(0);
+}
+
+RETSIGTYPE
+sigquit_handler(int a)
+{
+ if(debug_lvl > DEBUG_NOTHING)
+ syslog(LOG_NOTICE, _("Got QUIT signal"));
+ cleanup_and_exit(0);
+}
+
+RETSIGTYPE
+fatal_signal_square(int a)
+{
+ syslog(LOG_ERR, _("Got another fatal signal %d (%s): not restarting."), a, strsignal(a));
+ cp_trace();
+ exit(1);
+}
+
+RETSIGTYPE
+fatal_signal_handler(int a)
+{
+ struct sigaction act;
+ syslog(LOG_ERR, _("Got fatal signal %d (%s)"), a, strsignal(a));
+ cp_trace();
+
+ if(do_detach)
+ {
+ syslog(LOG_NOTICE, _("Trying to re-execute in 5 seconds..."));
+
+ act.sa_handler = fatal_signal_square;
+ act.sa_mask = emptysigset;
+ act.sa_flags = 0;
+ sigaction(SIGSEGV, &act, NULL);
+
+ close_network_connections();
+ sleep(5);
+ remove_pid(pidfilename);
+ execvp(g_argv[0], g_argv);
+ }
+ else
+ {
+ syslog(LOG_NOTICE, _("Not restarting."));
+ exit(1);
+ }
+}
+
+RETSIGTYPE
+sighup_handler(int a)
+{
+ if(debug_lvl > DEBUG_NOTHING)
+ syslog(LOG_NOTICE, _("Got HUP signal"));
+ sighup = 1;
+}
+
+RETSIGTYPE
+sigint_handler(int a)
+{
+ if(saved_debug_lvl)
+ {
+ syslog(LOG_NOTICE, _("Reverting to old debug level (%d)"),
+ saved_debug_lvl);
+ debug_lvl = saved_debug_lvl;
+ saved_debug_lvl = 0;
+ }
+ else
+ {
+ syslog(LOG_NOTICE, _("Temporarily setting debug level to 5. Kill me with SIGINT again to go back to level %d."),
+ debug_lvl);
+ saved_debug_lvl = debug_lvl;
+ debug_lvl = 5;
+ }
+}
+
+RETSIGTYPE
+sigalrm_handler(int a)
+{
+ if(debug_lvl > DEBUG_NOTHING)
+ syslog(LOG_NOTICE, _("Got ALRM signal"));
+ sigalrm = 1;
+}
+
+RETSIGTYPE
+sigusr1_handler(int a)
+{
+ dump_connections();
+}
+
+RETSIGTYPE
+sigusr2_handler(int a)
+{
+ dump_device_stats();
+ dump_nodes();
+ dump_edges();
+ dump_subnets();
+}
+
+RETSIGTYPE
+sigwinch_handler(int a)
+{
+ extern int do_purge;
+ do_purge = 1;
+}
+
+RETSIGTYPE
+unexpected_signal_handler(int a)
+{
+ syslog(LOG_WARNING, _("Got unexpected signal %d (%s)"), a, strsignal(a));
+ cp_trace();
+}
+
+RETSIGTYPE
+ignore_signal_handler(int a)
+{
+ if(debug_lvl >= DEBUG_SCARY_THINGS)
+ {
+ syslog(LOG_DEBUG, _("Ignored signal %d (%s)"), a, strsignal(a));
+ cp_trace();
+ }
+}
+
+struct {
+ int signal;
+ void (*handler)(int);
+} sighandlers[] = {
+ { SIGHUP, sighup_handler },
+ { SIGTERM, sigterm_handler },
+ { SIGQUIT, sigquit_handler },
+ { SIGSEGV, fatal_signal_handler },
+ { SIGBUS, fatal_signal_handler },
+ { SIGILL, fatal_signal_handler },
+ { SIGPIPE, ignore_signal_handler },
+ { SIGINT, sigint_handler },
+ { SIGUSR1, sigusr1_handler },
+ { SIGUSR2, sigusr2_handler },
+ { SIGCHLD, ignore_signal_handler },
+ { SIGALRM, sigalrm_handler },
+ { SIGWINCH, sigwinch_handler },
+ { 0, NULL }
+};
+
+void
+setup_signals(void)
+{
+ int i;
+ struct sigaction act;
+
+ sigemptyset(&emptysigset);
+ act.sa_handler = NULL;
+ act.sa_mask = emptysigset;
+ act.sa_flags = 0;
+
+ /* Set a default signal handler for every signal, errors will be
+ ignored. */
+ for(i = 0; i < NSIG; i++)
+ {
+ if(!do_detach)
+ act.sa_handler = SIG_DFL;
+ else
+ act.sa_handler = unexpected_signal_handler;
+ sigaction(i, &act, NULL);
+ }
+
+ /* If we didn't detach, allow coredumps */
+ if(!do_detach)
+ sighandlers[3].handler = SIG_DFL;
+
+ /* Then, for each known signal that we want to catch, assign a
+ handler to the signal, with error checking this time. */
+ for(i = 0; sighandlers[i].signal; i++)
+ {
+ act.sa_handler = sighandlers[i].handler;
+ if(sigaction(sighandlers[i].signal, &act, NULL) < 0)
+ fprintf(stderr, _("Installing signal handler for signal %d (%s) failed: %s\n"),
+ sighandlers[i].signal, strsignal(sighandlers[i].signal), strerror(errno));
+ }
+}
--- /dev/null
+/*
+ process.h -- header file for process.c
+ Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
+ 2000-2002 Guus Sliepen <guus@sliepen.warande.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: process.h,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+#ifndef __TINC_PROCESS_H__
+#define __TINC_PROCESS_H__
+
+#include "config.h"
+
+extern int do_detach;
+
+extern void setup_signals(void);
+extern int execute_script(const char *);
+extern int detach(void);
+extern int kill_other(int);
+extern void cleanup_and_exit(int);
+
+#endif /* __TINC_PROCESS_H__ */
/*
- protocol.c -- handle the meta-protocol
- Copyright (C) 1999,2000 Ivo Timmermans <itimmermans@bigfoot.com>,
- 2000 Guus Sliepen <guus@sliepen.warande.net>
+ protocol.c -- handle the meta-protocol, basic functions
+ Copyright (C) 1999-2001 Ivo Timmermans <itimmermans@bigfoot.com>,
+ 2000,2001 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- $Id: protocol.c,v 1.29 2000/10/18 20:12:09 zarq Exp $
+ $Id: protocol.c,v 1.30 2002/04/09 15:26:00 zarq Exp $
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
-#include <sys/socket.h>
-#include <unistd.h>
#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
#include <utils.h>
#include <xalloc.h>
-#include <netinet/in.h>
-
-#include <openssl/sha.h>
-
#include "conf.h"
-#include "encr.h"
-#include "net.h"
-#include "netutl.h"
#include "protocol.h"
#include "meta.h"
+#include "connection.h"
#include "system.h"
+avl_tree_t *past_request_tree;
+
int check_id(char *id)
{
int i;
for (i = 0; i < strlen(id); i++)
if(!isalnum(id[i]) && id[i] != '_')
return -1;
-
+
return 0;
}
-/* Generic request routines - takes care of logging and error detection as well */
+/* Generic request routines - takes care of logging and error
+ detection as well */
-int send_request(conn_list_t *cl, const char *format, ...)
+int send_request(connection_t *c, const char *format, ...)
{
va_list args;
char buffer[MAXBUFSIZE];
int len, request;
cp
- /* Use vsnprintf instead of vasprintf: faster, no memory fragmentation, cleanup is automatic,
- and there is a limit on the input buffer anyway */
+ /* Use vsnprintf instead of vasprintf: faster, no memory
+ fragmentation, cleanup is automatic, and there is a limit on the
+ input buffer anyway */
va_start(args, format);
len = vsnprintf(buffer, MAXBUFSIZE, format, args);
- request = va_arg(args, int);
va_end(args);
if(len < 0 || len > MAXBUFSIZE-1)
{
- syslog(LOG_ERR, _("Output buffer overflow while sending %s to %s (%s)"), request_name[request], cl->name, cl->hostname);
+ syslog(LOG_ERR, _("Output buffer overflow while sending request to %s (%s)"), c->name, c->hostname);
return -1;
}
- len++;
-
if(debug_lvl >= DEBUG_PROTOCOL)
- syslog(LOG_DEBUG, _("Sending %s to %s (%s)"), request_name[request], cl->name, cl->hostname);
+ {
+ sscanf(buffer, "%d", &request);
+ if(debug_lvl >= DEBUG_META)
+ syslog(LOG_DEBUG, _("Sending %s to %s (%s): %s"), request_name[request], c->name, c->hostname, buffer);
+ else
+ syslog(LOG_DEBUG, _("Sending %s to %s (%s)"), request_name[request], c->name, c->hostname);
+ }
+ buffer[len++] = '\n';
cp
- return send_meta(cl, buffer, len);
+ return send_meta(c, buffer, len);
}
-int receive_request(conn_list_t *cl)
+int receive_request(connection_t *c)
{
int request;
-cp
- if(sscanf(cl->buffer, "%d", &request) == 1)
+cp
+ if(sscanf(c->buffer, "%d", &request) == 1)
{
- if((request < 0) || (request > 255) || (request_handlers[request] == NULL))
+ if((request < 0) || (request >= LAST) || (request_handlers[request] == NULL))
{
- syslog(LOG_ERR, _("Unknown request from %s (%s)"),
- cl->name, cl->hostname);
+ if(debug_lvl >= DEBUG_META)
+ syslog(LOG_DEBUG, _("Unknown request from %s (%s): %s"),
+ c->name, c->hostname, c->buffer);
+ else
+ syslog(LOG_ERR, _("Unknown request from %s (%s)"),
+ c->name, c->hostname);
+
return -1;
}
else
{
- if(debug_lvl > DEBUG_PROTOCOL)
- syslog(LOG_DEBUG, _("Got %s from %s (%s)"),
- request_name[request], cl->name, cl->hostname);
+ if(debug_lvl >= DEBUG_PROTOCOL)
+ {
+ if(debug_lvl >= DEBUG_META)
+ syslog(LOG_DEBUG, _("Got %s from %s (%s): %s"),
+ request_name[request], c->name, c->hostname, c->buffer);
+ else
+ syslog(LOG_DEBUG, _("Got %s from %s (%s)"),
+ request_name[request], c->name, c->hostname);
+ }
}
- if(request_handlers[request](cl))
+
+ if((c->allow_request != ALL) && (c->allow_request != request))
+ {
+ syslog(LOG_ERR, _("Unauthorized request from %s (%s)"), c->name, c->hostname);
+ return -1;
+ }
+
+ if(request_handlers[request](c))
/* Something went wrong. Probably scriptkiddies. Terminate. */
{
syslog(LOG_ERR, _("Error while processing %s from %s (%s)"),
- request_name[request], cl->name, cl->hostname);
+ request_name[request], c->name, c->hostname);
return -1;
}
}
else
{
syslog(LOG_ERR, _("Bogus data received from %s (%s)"),
- cl->name, cl->hostname);
+ c->name, c->hostname);
return -1;
}
-}
-
-/* Connection protocol:
-
- Client Server
- send_id(u)
- send_challenge(R)
- send_chal_reply(H)
- send_id(u)
- send_challenge(R)
- send_chal_reply(H)
- ---------------------------------------
- Any negotations about the meta protocol
- encryption go here(u).
- ---------------------------------------
- send_ack(u)
- send_ack(u)
- ---------------------------------------
- Other requests(E)...
-
- (u) Unencrypted,
- (R) RSA,
- (H) SHA1,
- (E) Encrypted with symmetric cipher.
-
- Part of the challenge is directly used to set the symmetric cipher key and the initial vector.
- Since a man-in-the-middle cannot decrypt the RSA challenges, this means that he cannot get or
- forge the key for the symmetric cipher.
-*/
-
-int send_id(conn_list_t *cl)
-{
-cp
- cl->allow_request = CHALLENGE;
cp
- return send_request(cl, "%d %s %d %lx %hd", ID, myself->name, myself->protocol_version, myself->options, myself->port);
+ return 0;
}
-int id_h(conn_list_t *cl)
+int past_request_compare(past_request_t *a, past_request_t *b)
{
- conn_list_t *old;
-cp
- if(sscanf(cl->buffer, "%*d %as %d %lx %hd", &cl->name, &cl->protocol_version, &cl->options, &cl->port) != 4)
- {
- syslog(LOG_ERR, _("Got bad ID from %s"), cl->hostname);
- return -1;
- }
-
- /* Check if version matches */
-
- if(cl->protocol_version != myself->protocol_version)
- {
- syslog(LOG_ERR, _("Peer %s (%s) uses incompatible version %d"),
- cl->name, cl->hostname, cl->protocol_version);
- return -1;
- }
-
- /* Check if identity is a valid name */
-
- if(check_id(cl->name))
- {
- syslog(LOG_ERR, _("Peer %s uses invalid identity name"), cl->hostname);
- return -1;
- }
-
- /* Load information about peer */
-
- if(read_host_config(cl))
- {
- syslog(LOG_ERR, _("Peer %s had unknown identity (%s)"), cl->hostname, cl->name);
- return -1;
- }
-
-
- /* First check if the host we connected to is already in our
- connection list. If so, we are probably making a loop, which
- is not desirable.
- */
-
- if(cl->status.outgoing)
- {
- if((old = lookup_id(cl->name)))
- {
- if(debug_lvl > DEBUG_CONNECTIONS)
- syslog(LOG_NOTICE, _("Uplink %s (%s) is already in our connection list"), cl->name, cl->hostname);
- cl->status.outgoing = 0;
- old->status.outgoing = 1;
- terminate_connection(cl);
- return 0;
- }
- }
cp
- return send_challenge(cl);
+ return strcmp(a->request, b->request);
}
-int send_challenge(conn_list_t *cl)
+void free_past_request(past_request_t *r)
{
- char buffer[CHAL_LENGTH*2+1];
-cp
- /* Allocate buffers for the challenge */
-
- if(!cl->hischallenge)
- cl->hischallenge = xmalloc(CHAL_LENGTH);
-cp
- /* Copy random data to the buffer */
-
- RAND_bytes(cl->hischallenge, CHAL_LENGTH);
cp
- /* Convert the random data to a hexadecimal formatted string */
-
- bin2hex(cl->hischallenge, buffer, CHAL_LENGTH);
- buffer[CHAL_LENGTH*2] = '\0';
-
- /* Send the challenge */
-
- cl->allow_request = CHAL_REPLY;
+ if(r->request)
+ free(r->request);
+ free(r);
cp
- return send_request(cl, "%d %s", CHALLENGE, buffer);
}
-int challenge_h(conn_list_t *cl)
+void init_requests(void)
{
- char *buffer;
cp
- if(sscanf(cl->buffer, "%*d %as", &buffer) != 1)
- {
- syslog(LOG_ERR, _("Got bad CHALLENGE from %s (%s)"), cl->name, cl->hostname);
- return -1;
- }
-
- /* Check if the length of the challenge is all right */
-
- if(strlen(buffer) != CHAL_LENGTH*2)
- {
- syslog(LOG_ERR, _("Intruder: wrong challenge length from %s (%s)"), cl->name, cl->hostname);
- free(buffer);
- return -1;
- }
-
- /* Allocate buffers for the challenge */
-
- if(!cl->mychallenge)
- cl->mychallenge = xmalloc(CHAL_LENGTH);
-
- /* Convert the challenge from hexadecimal back to binary */
-
- hex2bin(buffer,cl->mychallenge,CHAL_LENGTH);
- free(buffer);
-
- /* Rest is done by send_chal_reply() */
+ past_request_tree = avl_alloc_tree((avl_compare_t)past_request_compare, (avl_action_t)free_past_request);
cp
- return send_chal_reply(cl);
}
-int send_chal_reply(conn_list_t *cl)
+void exit_requests(void)
{
- char hash[SHA_DIGEST_LENGTH*2+1];
cp
- if(!cl->mychallenge)
- {
- syslog(LOG_ERR, _("Trying to send CHAL_REPLY to %s (%s) without a valid CHALLENGE"), cl->name, cl->hostname);
- return -1;
- }
-
- /* Calculate the hash from the challenge we received */
-
- SHA1(cl->mychallenge, CHAL_LENGTH, hash);
-
- /* Convert the hash to a hexadecimal formatted string */
-
- bin2hex(hash,hash,SHA_DIGEST_LENGTH);
- hash[SHA_DIGEST_LENGTH*2] = '\0';
-
- /* Send the reply */
-
- if(cl->status.outgoing)
- cl->allow_request = ID;
- else
- cl->allow_request = ACK;
-
+ avl_delete_tree(past_request_tree);
cp
- return send_request(cl, "%d %s", CHAL_REPLY, hash);
}
-int chal_reply_h(conn_list_t *cl)
+int seen_request(char *request)
{
- char *hishash;
- char myhash[SHA_DIGEST_LENGTH];
+ past_request_t p, *new;
cp
- if(sscanf(cl->buffer, "%*d %as", &hishash) != 1)
- {
- syslog(LOG_ERR, _("Got bad CHAL_REPLY from %s (%s)"), cl->name, cl->hostname);
- free(hishash);
- return -1;
- }
-
- /* Check if the length of the hash is all right */
+ p.request = request;
- if(strlen(hishash) != SHA_DIGEST_LENGTH*2)
+ if(avl_search(past_request_tree, &p))
{
- syslog(LOG_ERR, _("Intruder: wrong challenge reply length from %s (%s)"), cl->name, cl->hostname);
- free(hishash);
- return -1;
- }
-
- /* Convert the hash to binary format */
-
- hex2bin(hishash, hishash, SHA_DIGEST_LENGTH);
-
- /* Calculate the hash from the challenge we sent */
-
- SHA1(cl->hischallenge, CHAL_LENGTH, myhash);
-
- /* Verify the incoming hash with the calculated hash */
-
- if(memcmp(hishash, myhash, SHA_DIGEST_LENGTH))
- {
- syslog(LOG_ERR, _("Intruder: wrong challenge reply from %s (%s)"), cl->name, cl->hostname);
- free(hishash);
- return -1;
+ if(debug_lvl >= DEBUG_SCARY_THINGS)
+ syslog(LOG_DEBUG, _("Already seen request"));
+ return 1;
}
-
- free(hishash);
-
- /* Identity has now been positively verified.
- If we are accepting this new connection, then send our identity,
- if we are making this connecting, acknowledge.
- */
-cp
- if(cl->status.outgoing)
- return send_ack(cl);
else
- return send_id(cl);
-}
-
-int send_ack(conn_list_t *cl)
-{
-cp
- cl->allow_request = ACK;
-cp
- return send_request(cl, "%d", ACK);
-}
-
-int ack_h(conn_list_t *cl)
-{
- conn_list_t *old;
-cp
- /* Okay, before we active the connection, we check if there is another entry
- in the connection list with the same name. If so, it presumably is an
- old connection that has timed out but we don't know it yet.
- */
-
- while((old = lookup_id(cl->name)))
{
- if(debug_lvl > DEBUG_CONNECTIONS)
- syslog(LOG_NOTICE, _("Removing old entry for %s at %s in favour of new connection from %s"),
- cl->name, old->hostname, cl->hostname);
- old->status.active = 0;
- terminate_connection(old);
- }
-
- /* Activate this connection */
-
- cl->allow_request = ALL;
- cl->status.active = 1;
-
- if(debug_lvl > DEBUG_CONNECTIONS)
- syslog(LOG_NOTICE, _("Connection with %s (%s) activated"), cl->name, cl->hostname);
-
- /* Exchange information about other tinc daemons */
-
-/* FIXME: reprogram this.
- notify_others(cl, NULL, send_add_host);
- notify_one(cl);
-*/
- upstreamindex = 0;
-
-cp
- if(cl->status.outgoing)
- return 0;
- else
- return send_ack(cl);
-}
-
-/* Address and subnet information exchange */
-
-int send_add_subnet(conn_list_t *cl, conn_list_t *other, subnet_t *subnet)
-{
- int x;
- char *netstr;
-cp
- x = send_request(cl, "%d %s %s", ADD_SUBNET,
- other->name, netstr = net2str(subnet));
- free(netstr);
-cp
- return x;
-}
-
-int add_subnet_h(conn_list_t *cl)
-{
- char *subnetstr;
- char *name;
- conn_list_t *owner;
- subnet_t *subnet, *old;
-cp
- if(sscanf(cl->buffer, "%*d %as %as", &name, &subnetstr) != 3)
- {
- syslog(LOG_ERR, _("Got bad ADD_SUBNET from %s (%s)"), cl->name, cl->hostname);
- free(name); free(subnetstr);
- return -1;
- }
-
- /* Check if owner name is a valid */
-
- if(check_id(name))
- {
- syslog(LOG_ERR, _("Got bad ADD_SUBNET from %s (%s): invalid identity name"), cl->name, cl->hostname);
- free(name); free(subnetstr);
- return -1;
- }
-
- /* Check if subnet string is valid */
-
- if(!(subnet = str2net(subnetstr)))
- {
- syslog(LOG_ERR, _("Got bad ADD_SUBNET from %s (%s): invalid subnet string"), cl->name, cl->hostname);
- free(name); free(subnetstr);
- return -1;
- }
-
- free(subnetstr);
-
- /* Check if somebody tries to add a subnet of ourself */
-
- if(!strcmp(name, myself->name))
- {
- syslog(LOG_ERR, _("Warning: got ADD_SUBNET from %s (%s) for ourself, restarting"),
- cl->name, cl->hostname);
- free(name);
- sighup = 1;
+ new = (past_request_t *)xmalloc(sizeof(*new));
+ new->request = xstrdup(request);
+ new->firstseen = now;
+ avl_insert(past_request_tree, new);
return 0;
}
-
- /* Check if the owner of the new subnet is in the connection list */
-
- if(!(owner = lookup_id(name)))
- {
- syslog(LOG_ERR, _("Got ADD_SUBNET for %s from %s (%s) which is not in our connection list"),
- name, cl->name, cl->hostname);
- free(name);
- return -1;
- }
-
- /* If everything is correct, add the subnet to the list of the owner */
-
- subnet_add(owner, subnet);
-cp
- return 0;
-}
-
-int send_del_subnet(conn_list_t *cl, conn_list_t *other, subnet_t *subnet)
-{
-cp
- return send_request(cl, "%d %s %s", DEL_SUBNET, other->name, net2str(subnet));
-}
-
-int del_subnet_h(conn_list_t *cl)
-{
- char *subnetstr;
- char *name;
- conn_list_t *owner;
- subnet_t *subnet, *old;
-cp
- if(sscanf(cl->buffer, "%*d %as %as", &name, &subnetstr) != 3)
- {
- syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s)"), cl->name, cl->hostname);
- free(name); free(subnetstr);
- return -1;
- }
-
- /* Check if owner name is a valid */
-
- if(check_id(name))
- {
- syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s): invalid identity name"), cl->name, cl->hostname);
- free(name); free(subnetstr);
- return -1;
- }
-
- /* Check if subnet string is valid */
-
- if(!(subnet = str2net(subnetstr)))
- {
- syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s): invalid subnet string"), cl->name, cl->hostname);
- free(name); free(subnetstr);
- return -1;
- }
-
- free(subnetstr);
-
- /* Check if somebody tries to add a subnet of ourself */
-
- if(!strcmp(name, myself->name))
- {
- syslog(LOG_ERR, _("Warning: got DEL_SUBNET from %s (%s) for ourself, restarting"),
- cl->name, cl->hostname);
- free(name);
- sighup = 1;
- return 0;
- }
-
- /* Check if the owner of the new subnet is in the connection list */
-
- if(!(owner = lookup_id(name)))
- {
- syslog(LOG_ERR, _("Got DEL_SUBNET for %s from %s (%s) which is not in our connection list"),
- name, cl->name, cl->hostname);
- free(name);
- return -1;
- }
-
- /* If everything is correct, delete the subnet from the list of the owner */
-
- subnet_del(subnet);
-cp
- return 0;
-}
-
-/* New and closed connections notification */
-
-int send_add_host(conn_list_t *cl, conn_list_t *other)
-{
-cp
- return send_request(cl, "%d %s %s %lx:%d %lx", ADD_HOST,
- myself->name, other->name, other->address, other->port, other->options);
+cp
}
-int add_host_h(conn_list_t *cl)
+void age_past_requests(void)
{
- char *sender;
- conn_list_t *old, *new, *hisuplink;
-cp
- new = new_conn_list();
-
- if(sscanf(cl->buffer, "%*d %as %as %lx:%d %lx", &sender, &new->name, &new->address, &new->port, &new->options) != 5)
- {
- syslog(LOG_ERR, _("Got bad ADD_HOST from %s (%s)"), cl->name, cl->hostname);
- return -1;
- }
-
- /* Check if identity is a valid name */
-
- if(check_id(new->name) || check_id(sender))
- {
- syslog(LOG_ERR, _("Got bad ADD_HOST from %s (%s): invalid identity name"), cl->name, cl->hostname);
- free(sender);
- return -1;
- }
-
- /* Check if somebody tries to add ourself */
-
- if(!strcmp(new->name, myself->name))
- {
- syslog(LOG_ERR, _("Warning: got ADD_HOST from %s (%s) for ourself, restarting"), cl->name, cl->hostname);
- sighup = 1;
- free(sender);
- return 0;
- }
-
- /* We got an ADD_HOST from ourself!? */
-
- if(!strcmp(sender, myself->name))
- {
- syslog(LOG_ERR, _("Warning: got ADD_HOST from %s (%s) from ourself, restarting"), cl->name, cl->hostname);
- sighup = 1;
- free(sender);
- return 0;
- }
-
- /* Lookup his uplink */
-
- if(!(new->hisuplink = lookup_id(sender)))
- {
- syslog(LOG_ERR, _("Got ADD_HOST from %s (%s) with origin %s which is not in our connection list"),
- sender, cl->name, cl->hostname);
- free(sender);
- return -1;
- }
-
- free(sender);
-
- /* Fill in more of the new conn_list structure */
-
- new->hostname = hostlookup(htonl(new->address));
-
- /* Check if the new host already exists in the connnection list */
-
- if((old = lookup_id(new->name)))
+ avl_node_t *node, *next;
+ past_request_t *p;
+ int left = 0, deleted = 0;
+cp
+ for(node = past_request_tree->head; node; node = next)
{
- if((new->address == old->address) && (new->port == old->port))
- {
- if(debug_lvl > DEBUG_CONNECTIONS)
- syslog(LOG_NOTICE, _("Got duplicate ADD_HOST for %s (%s) from %s (%s)"),
- old->name, old->hostname, new->name, new->hostname);
- return 0;
- }
+ next = node->next;
+ p = (past_request_t *)node->data;
+ if(p->firstseen + pingtimeout < now)
+ avl_delete_node(past_request_tree, node), deleted++;
else
- {
- if(debug_lvl > DEBUG_CONNECTIONS)
- syslog(LOG_NOTICE, _("Removing old entry for %s (%s)"),
- old->name, old->hostname);
- old->status.active = 0;
- terminate_connection(old);
- }
- }
-
- /* Fill in rest of conn_list structure */
-
- new->myuplink = cl;
- new->status.active = 1;
-
- /* Hook it up into the conn_list */
-
- conn_list_add(conn_list, new);
-
- /* Tell the rest about the new host */
-/* FIXME: reprogram this.
- notify_others(new, cl, send_add_host);
-*/
-cp
- return 0;
-}
-
-int send_del_host(conn_list_t *cl, conn_list_t *other)
-{
-cp
- return send_request(cl, "%d %s %s %lx:%d %lx", DEL_HOST,
- myself->name, other->name, other->address, other->port, other->options);
-}
-
-int del_host_h(conn_list_t *cl)
-{
- char *name;
- char *sender;
- ip_t address;
- port_t port;
- int options;
- conn_list_t *old, *hisuplink;
-
-cp
- if(sscanf(cl->buffer, "%*d %as %as %lx:%d %lx", &sender, &name, &address, &port, &options) != 5)
- {
- syslog(LOG_ERR, _("Got bad DEL_HOST from %s (%s)"),
- cl->name, cl->hostname);
- return -1;
- }
-
- /* Check if identity is a valid name */
-
- if(check_id(name) || check_id(sender))
- {
- syslog(LOG_ERR, _("Got bad DEL_HOST from %s (%s): invalid identity name"), cl->name, cl->hostname);
- free(name); free(sender);
- return -1;
- }
-
- /* Check if somebody tries to delete ourself */
-
- if(!strcmp(name, myself->name))
- {
- syslog(LOG_ERR, _("Warning: got DEL_HOST from %s (%s) for ourself, restarting"),
- cl->name, cl->hostname);
- free(name); free(sender);
- sighup = 1;
- return 0;
- }
-
- /* We got an ADD_HOST from ourself!? */
-
- if(!strcmp(sender, myself->name))
- {
- syslog(LOG_ERR, _("Warning: got DEL_HOST from %s (%s) from ourself, restarting"), cl->name, cl->hostname);
- sighup = 1;
- free(name); free(sender);
- return 0;
- }
-
- /* Lookup his uplink */
-
- if(!(hisuplink = lookup_id(sender)))
- {
- syslog(LOG_ERR, _("Got DEL_HOST from %s (%s) with origin %s which is not in our connection list"),
- cl->name, cl->hostname, sender);
- free(name); free(sender);
- return -1;
- }
-
- free(sender);
-
- /* Check if the new host already exists in the connnection list */
-
- if(!(old = lookup_id(name)))
- {
- syslog(LOG_ERR, _("Got DEL_HOST from %s (%s) for %s which is not in our connection list"),
- name, cl->name, cl->hostname);
- free(name);
- return -1;
+ left++;
}
-
- /* Check if the rest matches */
-
- if(address!=old->address || port!=old->port || options!=old->options || hisuplink!=old->hisuplink || cl!=old->myuplink)
- {
- syslog(LOG_WARNING, _("Got DEL_HOST from %s (%s) for %s which doesn't match"), cl->name, cl->hostname, old->name);
- return 0;
- }
-
- /* Ok, since EVERYTHING seems to check out all right, delete it */
-
- old->status.termreq = 1;
- old->status.active = 0;
-
- terminate_connection(old);
-cp
- return 0;
-}
-
-/* Status and error notification routines */
-
-int send_status(conn_list_t *cl, int statusno, char *statusstring)
-{
-cp
- if(!statusstring)
- statusstring = status_text[statusno];
-cp
- return send_request(cl, "%d %d %s", STATUS, statusno, statusstring);
-}
-
-int status_h(conn_list_t *cl)
-{
- int statusno;
- char *statusstring;
-cp
- if(sscanf(cl->buffer, "%*d %d %as", &statusno, &statusstring) != 2)
- {
- syslog(LOG_ERR, _("Got bad STATUS from %s (%s)"),
- cl->name, cl->hostname);
- return -1;
- }
-
- if(debug_lvl > DEBUG_STATUS)
- {
- syslog(LOG_NOTICE, _("Status message from %s (%s): %s: %s"),
- cl->name, cl->hostname, status_text[statusno], statusstring);
- }
-
-cp
- free(statusstring);
- return 0;
-}
-int send_error(conn_list_t *cl, int errno, char *errstring)
-{
+ if(debug_lvl >= DEBUG_SCARY_THINGS && left + deleted)
+ syslog(LOG_DEBUG, _("Aging past requests: deleted %d, left %d\n"), deleted, left);
cp
- if(!errstring)
- errstring = strerror(errno);
- return send_request(cl, "%d %d %s", ERROR, errno, errstring);
-}
-
-int error_h(conn_list_t *cl)
-{
- int errno;
- char *errorstring;
-cp
- if(sscanf(cl->buffer, "%*d %d %as", &errno, &errorstring) != 2)
- {
- syslog(LOG_ERR, _("Got bad ERROR from %s (%s)"),
- cl->name, cl->hostname);
- return -1;
- }
-
- if(debug_lvl > DEBUG_ERROR)
- {
- syslog(LOG_NOTICE, _("Error message from %s (%s): %s: %s"),
- cl->name, cl->hostname, strerror(errno), errorstring);
- }
-
- free(errorstring);
- cl->status.termreq = 1;
- terminate_connection(cl);
-cp
- return 0;
-}
-
-int send_termreq(conn_list_t *cl)
-{
-cp
- return send_request(cl, "%d", TERMREQ);
-}
-
-int termreq_h(conn_list_t *cl)
-{
-cp
- cl->status.termreq = 1;
- terminate_connection(cl);
-cp
- return 0;
-}
-
-/* Keepalive routines - FIXME: needs a closer look */
-
-int send_ping(conn_list_t *cl)
-{
- cl->status.pinged = 1;
-cp
- return send_request(cl, "%d", PING);
-}
-
-int ping_h(conn_list_t *cl)
-{
-cp
- return send_pong(cl);
-}
-
-int send_pong(conn_list_t *cl)
-{
-cp
- return send_request(cl, "%d", PONG);
-}
-
-int pong_h(conn_list_t *cl)
-{
-cp
- cl->status.got_pong = 1;
-cp
- return 0;
-}
-
-/* Key exchange */
-
-int send_key_changed(conn_list_t *from, conn_list_t *cl)
-{
- conn_list_t *p;
-cp
- for(p = conn_list; p != NULL; p = p->next)
- {
- if(p!=cl && p->status.meta && p->status.active)
- send_request(p, "%d %s", KEY_CHANGED,
- from->name);
- }
-cp
- return 0;
-}
-
-int key_changed_h(conn_list_t *cl)
-{
- char *from_id;
- conn_list_t *from;
-cp
- if(sscanf(cl->buffer, "%*d %as", &from_id) != 1)
- {
- syslog(LOG_ERR, _("Got bad KEY_CHANGED from %s (%s)"),
- cl->name, cl->hostname);
- return -1;
- }
-
- if(!(from = lookup_id(from_id)))
- {
- syslog(LOG_ERR, _("Got KEY_CHANGED from %s (%s) origin %s which does not exist in our connection list"),
- cl->name, cl->hostname, from_id);
- free(from_id);
- return -1;
- }
-
- free(from_id);
-
- from->status.validkey = 0;
- from->status.waitingforkey = 0;
-
- send_key_changed(from, cl);
-cp
- return 0;
-}
-
-int send_req_key(conn_list_t *from, conn_list_t *to)
-{
-cp
- return send_request(to->nexthop, "%d %s %s", REQ_KEY,
- from->name, to->name);
-}
-
-int req_key_h(conn_list_t *cl)
-{
- char *from_id, *to_id;
- conn_list_t *from, *to;
-cp
- if(sscanf(cl->buffer, "%*d %as %as", &from_id, &to_id) != 2)
- {
- syslog(LOG_ERR, _("Got bad REQ_KEY from %s (%s)"),
- cl->name, cl->hostname);
- return -1;
- }
-
- if(!(from = lookup_id(from_id)))
- {
- syslog(LOG_ERR, _("Got REQ_KEY from %s (%s) origin %s which does not exist in our connection list"),
- cl->name, cl->hostname, from_id);
- free(from_id); free(to_id);
- return -1;
- }
-
- /* Check if this key request is for us */
-
- if(!strcmp(to_id, myself->name))
- {
- send_ans_key(myself, from, myself->cipher_pktkey);
- }
- else
- {
- if(!(to = lookup_id(to_id)))
- {
- syslog(LOG_ERR, _("Got REQ_KEY from %s (%s) destination %s which does not exist in our connection list"),
- cl->name, cl->hostname, to_id);
- free(from_id); free(to_id);
- return -1;
- }
- send_req_key(from, to);
- }
-
- free(from_id); free(to_id);
-cp
- return 0;
-}
-
-int send_ans_key(conn_list_t *from, conn_list_t *to, char *pktkey)
-{
-cp
- return send_request(to->nexthop, "%d %s %s %s", ANS_KEY,
- from->name, to->name, pktkey);
-}
-
-int ans_key_h(conn_list_t *cl)
-{
- char *from_id, *to_id, *pktkey;
- int keylength;
- conn_list_t *from, *to;
-cp
- if(sscanf(cl->buffer, "%*d %as %as %as", &from_id, &to_id, &pktkey) != 3)
- {
- syslog(LOG_ERR, _("Got bad ANS_KEY from %s (%s)"),
- cl->name, cl->hostname);
- return -1;
- }
-
- if(!(from = lookup_id(from_id)))
- {
- syslog(LOG_ERR, _("Got ANS_KEY from %s (%s) origin %s which does not exist in our connection list"),
- cl->name, cl->hostname, from_id);
- free(from_id); free(to_id); free(pktkey);
- return -1;
- }
-
- /* Check if this key request is for us */
-
- if(!strcmp(to_id, myself->name))
- {
- /* It is for us, convert it to binary and set the key with it. */
-
- keylength = strlen(pktkey);
-
- if((keylength%2) || (keylength <= 0))
- {
- syslog(LOG_ERR, _("Got bad ANS_KEY from %s (%s) origin %s: invalid key"),
- cl->name, cl->hostname, from->name);
- free(from_id); free(to_id); free(pktkey);
- return -1;
- }
- keylength /= 2;
- hex2bin(pktkey, pktkey, keylength);
- BF_set_key(cl->cipher_pktkey, keylength, pktkey);
- }
- else
- {
- if(!(to = lookup_id(to_id)))
- {
- syslog(LOG_ERR, _("Got ANS_KEY from %s (%s) destination %s which does not exist in our connection list"),
- cl->name, cl->hostname, to_id);
- free(from_id); free(to_id); free(pktkey);
- return -1;
- }
- send_ans_key(from, to, pktkey);
- }
-
- free(from_id); free(to_id); free(pktkey);
-cp
- return 0;
}
/* Jumptable for the request handlers */
-int (*request_handlers[])(conn_list_t*) = {
- id_h, challenge_h, chal_reply_h, ack_h,
+int (*request_handlers[])(connection_t*) = {
+ id_h, metakey_h, challenge_h, chal_reply_h, ack_h,
status_h, error_h, termreq_h,
ping_h, pong_h,
- add_host_h, del_host_h,
add_subnet_h, del_subnet_h,
+ add_edge_h, del_edge_h,
key_changed_h, req_key_h, ans_key_h,
+ tcppacket_h,
};
/* Request names */
char (*request_name[]) = {
- "ID", "CHALLENGE", "CHAL_REPLY", "ACK",
+ "ID", "METAKEY", "CHALLENGE", "CHAL_REPLY", "ACK",
"STATUS", "ERROR", "TERMREQ",
"PING", "PONG",
- "ADD_HOST", "DEL_HOST",
"ADD_SUBNET", "DEL_SUBNET",
+ "ADD_EDGE", "DEL_EDGE",
"KEY_CHANGED", "REQ_KEY", "ANS_KEY",
-};
-
-/* Status strings */
-
-char (*status_text[]) = {
- "Warning",
-};
-
-/* Error strings */
-
-char (*error_text[]) = {
- "Error",
+ "PACKET",
};
/*
protocol.h -- header for protocol.c
- Copyright (C) 1999,2000 Ivo Timmermans <itimmermans@bigfoot.com>,
- 2000 Guus Sliepen <guus@sliepen.warande.net>
+ Copyright (C) 1999-2001 Ivo Timmermans <itimmermans@bigfoot.com>,
+ 2000,2001 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- $Id: protocol.h,v 1.6 2000/10/18 20:12:09 zarq Exp $
+ $Id: protocol.h,v 1.7 2002/04/09 15:26:00 zarq Exp $
*/
#ifndef __TINC_PROTOCOL_H__
#define __TINC_PROTOCOL_H__
#include "net.h"
+#include "node.h"
#include "subnet.h"
/* Protocol version. Different versions are incompatible,
incompatible version have different protocols.
*/
-#define PROT_CURRENT 8
-
-/* Length of the challenge. Since the challenge will also
- contain the key for the symmetric cipher, it must be
- quite large.
- */
-
-#define CHAL_LENGTH 1024 /* Okay, this is probably waaaaaaaaaaay too large */
+#define PROT_CURRENT 14
/* Request numbers */
enum {
ALL = -1, /* Guardian for allow_request */
- ID = 0, CHALLENGE, CHAL_REPLY, ACK,
+ ID = 0, METAKEY, CHALLENGE, CHAL_REPLY, ACK,
STATUS, ERROR, TERMREQ,
- PING, PONG,
- ADD_HOST, DEL_HOST,
+ PING, PONG,
+// ADD_NODE, DEL_NODE,
ADD_SUBNET, DEL_SUBNET,
+ ADD_EDGE, DEL_EDGE,
KEY_CHANGED, REQ_KEY, ANS_KEY,
+ PACKET,
LAST /* Guardian for the highest request number */
};
-extern int (*request_handlers[])(conn_list_t*);
-
-extern int send_id(conn_list_t*);
-extern int send_challenge(conn_list_t*);
-extern int send_chal_reply(conn_list_t*);
-extern int send_ack(conn_list_t*);
-extern int send_status(conn_list_t*, int, char*);
-extern int send_error(conn_list_t*, int, char*);
-extern int send_termreq(conn_list_t*);
-extern int send_ping(conn_list_t*);
-extern int send_pong(conn_list_t*);
-extern int send_add_host(conn_list_t*, conn_list_t*);
-extern int send_del_host(conn_list_t*, conn_list_t*);
-extern int send_add_subnet(conn_list_t*, conn_list_t*, subnet_t*);
-extern int send_del_subnet(conn_list_t*, conn_list_t*, subnet_t*);
-extern int send_key_changed(conn_list_t*, conn_list_t*);
-extern int send_req_key(conn_list_t*, conn_list_t*);
-extern int send_ans_key(conn_list_t*, conn_list_t*, char*);
-
-/* Old functions */
-
-extern int send_tcppacket(conn_list_t *, void *, int);
-extern int notify_others(conn_list_t *, conn_list_t *, int (*function)(conn_list_t*, conn_list_t*));
+typedef struct past_request_t {
+ char *request;
+ time_t firstseen;
+} past_request_t;
+
+/* Maximum size of strings in a request */
+
+#define MAX_STRING_SIZE 2048
+#define MAX_STRING "%2048s"
+
+/* Basic functions */
+
+extern int send_request(connection_t*, const char*, ...);
+extern int receive_request(connection_t *);
+extern int check_id(char *);
+
+extern void init_requests(void);
+extern void exit_requests(void);
+extern int seen_request(char *);
+extern void age_past_requests(void);
+
+/* Requests */
+
+extern int send_id(connection_t *);
+extern int send_metakey(connection_t *);
+extern int send_challenge(connection_t *);
+extern int send_chal_reply(connection_t *);
+extern int send_ack(connection_t *);
+extern int send_status(connection_t *, int, char *);
+extern int send_error(connection_t *, int, char *);
+extern int send_termreq(connection_t *);
+extern int send_ping(connection_t *);
+extern int send_pong(connection_t *);
+// extern int send_add_node(connection_t *, node_t *);
+// extern int send_del_node(connection_t *, node_t *);
+extern int send_add_subnet(connection_t *, subnet_t *);
+extern int send_del_subnet(connection_t *, subnet_t *);
+extern int send_add_edge(connection_t *, edge_t *);
+extern int send_del_edge(connection_t *, edge_t *);
+extern int send_key_changed(connection_t *, node_t *);
+extern int send_req_key(connection_t *, node_t *, node_t *);
+extern int send_ans_key(connection_t *, node_t *, node_t *);
+extern int send_tcppacket(connection_t *, vpn_packet_t *);
+
+/* Request handlers */
+
+extern int (*request_handlers[])(connection_t *);
+
+extern int id_h(connection_t *);
+extern int metakey_h(connection_t *);
+extern int challenge_h(connection_t *);
+extern int chal_reply_h(connection_t *);
+extern int ack_h(connection_t *);
+extern int status_h(connection_t *);
+extern int error_h(connection_t *);
+extern int termreq_h(connection_t *);
+extern int ping_h(connection_t *);
+extern int pong_h(connection_t *);
+// extern int add_node_h(connection_t *);
+// extern int del_node_h(connection_t *);
+extern int add_subnet_h(connection_t *);
+extern int del_subnet_h(connection_t *);
+extern int add_edge_h(connection_t *);
+extern int del_edge_h(connection_t *);
+extern int key_changed_h(connection_t *);
+extern int req_key_h(connection_t *);
+extern int ans_key_h(connection_t *);
+extern int tcppacket_h(connection_t *);
#endif /* __TINC_PROTOCOL_H__ */
--- /dev/null
+/*
+ protocol_auth.c -- handle the meta-protocol, authentication
+ Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
+ 2000-2002 Guus Sliepen <guus@sliepen.warande.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: protocol_auth.c,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include <utils.h>
+#include <xalloc.h>
+#include <avl_tree.h>
+
+#include <openssl/sha.h>
+#include <openssl/rand.h>
+#include <openssl/evp.h>
+
+#ifndef HAVE_RAND_PSEUDO_BYTES
+#define RAND_pseudo_bytes RAND_bytes
+#endif
+
+#include "conf.h"
+#include "net.h"
+#include "netutl.h"
+#include "protocol.h"
+#include "meta.h"
+#include "connection.h"
+#include "node.h"
+#include "edge.h"
+#include "graph.h"
+
+#include "system.h"
+
+int send_id(connection_t *c)
+{
+cp
+ return send_request(c, "%d %s %d", ID, myself->connection->name, myself->connection->protocol_version);
+}
+
+int id_h(connection_t *c)
+{
+ char name[MAX_STRING_SIZE];
+ int bla;
+cp
+ if(sscanf(c->buffer, "%*d "MAX_STRING" %d", name, &c->protocol_version) != 2)
+ {
+ syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ID", c->name, c->hostname);
+ return -1;
+ }
+
+ /* Check if identity is a valid name */
+
+ if(check_id(name))
+ {
+ syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ID", c->name, c->hostname, "invalid name");
+ return -1;
+ }
+
+ /* If we set c->name in advance, make sure we are connected to the right host */
+
+ if(c->name)
+ {
+ if(strcmp(c->name, name))
+ {
+ syslog(LOG_ERR, _("Peer %s is %s instead of %s"), c->hostname, name, c->name);
+ return -1;
+ }
+ }
+ else
+ c->name = xstrdup(name);
+
+ /* Check if version matches */
+
+ if(c->protocol_version != myself->connection->protocol_version)
+ {
+ syslog(LOG_ERR, _("Peer %s (%s) uses incompatible version %d"),
+ c->name, c->hostname, c->protocol_version);
+ return -1;
+ }
+
+ if(bypass_security)
+ {
+ if(!c->config_tree)
+ init_configuration(&c->config_tree);
+ c->allow_request = ACK;
+ return send_ack(c);
+ }
+
+ if(!c->config_tree)
+ {
+ init_configuration(&c->config_tree);
+
+ if((bla = read_connection_config(c)))
+ {
+ syslog(LOG_ERR, _("Peer %s had unknown identity (%s)"), c->hostname, c->name);
+ return -1;
+ }
+ }
+
+ if(read_rsa_public_key(c))
+ {
+ return -1;
+ }
+
+ /* Check some options */
+
+ if((get_config_bool(lookup_config(c->config_tree, "IndirectData"), &bla) && bla) || myself->options & OPTION_INDIRECT)
+ c->options |= OPTION_INDIRECT;
+
+ if((get_config_bool(lookup_config(c->config_tree, "TCPOnly"), &bla) && bla) || myself->options & OPTION_TCPONLY)
+ c->options |= OPTION_TCPONLY | OPTION_INDIRECT;
+
+ c->allow_request = METAKEY;
+cp
+ return send_metakey(c);
+}
+
+int send_metakey(connection_t *c)
+{
+ char buffer[MAX_STRING_SIZE];
+ int len, x;
+cp
+ len = RSA_size(c->rsa_key);
+
+ /* Allocate buffers for the meta key */
+
+ if(!c->outkey)
+ c->outkey = xmalloc(len);
+
+ if(!c->outctx)
+ c->outctx = xmalloc(sizeof(*c->outctx));
+cp
+ /* Copy random data to the buffer */
+
+ RAND_bytes(c->outkey, len);
+
+ /* The message we send must be smaller than the modulus of the RSA key.
+ By definition, for a key of k bits, the following formula holds:
+
+ 2^(k-1) <= modulus < 2^(k)
+
+ Where ^ means "to the power of", not "xor".
+ This means that to be sure, we must choose our message < 2^(k-1).
+ This can be done by setting the most significant bit to zero.
+ */
+
+ c->outkey[0] &= 0x7F;
+
+ if(debug_lvl >= DEBUG_SCARY_THINGS)
+ {
+ bin2hex(c->outkey, buffer, len);
+ buffer[len*2] = '\0';
+ syslog(LOG_DEBUG, _("Generated random meta key (unencrypted): %s"), buffer);
+ }
+
+ /* Encrypt the random data
+
+ We do not use one of the PKCS padding schemes here.
+ This is allowed, because we encrypt a totally random string
+ with a length equal to that of the modulus of the RSA key.
+ */
+
+ if(RSA_public_encrypt(len, c->outkey, buffer, c->rsa_key, RSA_NO_PADDING) != len)
+ {
+ syslog(LOG_ERR, _("Error during encryption of meta key for %s (%s)"), c->name, c->hostname);
+ return -1;
+ }
+cp
+ /* Convert the encrypted random data to a hexadecimal formatted string */
+
+ bin2hex(buffer, buffer, len);
+ buffer[len*2] = '\0';
+
+ /* Send the meta key */
+
+ x = send_request(c, "%d %d %d %d %d %s", METAKEY,
+ c->outcipher?c->outcipher->nid:0, c->outdigest?c->outdigest->type:0,
+ c->outmaclength, c->outcompression, buffer);
+
+ /* Further outgoing requests are encrypted with the key we just generated */
+
+ if(c->outcipher)
+ {
+ EVP_EncryptInit(c->outctx, c->outcipher,
+ c->outkey + len - c->outcipher->key_len,
+ c->outkey + len - c->outcipher->key_len - c->outcipher->iv_len);
+
+ c->status.encryptout = 1;
+ }
+cp
+ return x;
+}
+
+int metakey_h(connection_t *c)
+{
+ char buffer[MAX_STRING_SIZE];
+ int cipher, digest, maclength, compression;
+ int len;
+cp
+ if(sscanf(c->buffer, "%*d %d %d %d %d "MAX_STRING, &cipher, &digest, &maclength, &compression, buffer) != 5)
+ {
+ syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "METAKEY", c->name, c->hostname);
+ return -1;
+ }
+cp
+ len = RSA_size(myself->connection->rsa_key);
+
+ /* Check if the length of the meta key is all right */
+
+ if(strlen(buffer) != len*2)
+ {
+ syslog(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, c->hostname, "wrong keylength");
+ return -1;
+ }
+
+ /* Allocate buffers for the meta key */
+cp
+ if(!c->inkey)
+ c->inkey = xmalloc(len);
+
+ if(!c->inctx)
+ c->inctx = xmalloc(sizeof(*c->inctx));
+
+ /* Convert the challenge from hexadecimal back to binary */
+cp
+ hex2bin(buffer,buffer,len);
+
+ /* Decrypt the meta key */
+cp
+ if(RSA_private_decrypt(len, buffer, c->inkey, myself->connection->rsa_key, RSA_NO_PADDING) != len) /* See challenge() */
+ {
+ syslog(LOG_ERR, _("Error during encryption of meta key for %s (%s)"), c->name, c->hostname);
+ return -1;
+ }
+
+ if(debug_lvl >= DEBUG_SCARY_THINGS)
+ {
+ bin2hex(c->inkey, buffer, len);
+ buffer[len*2] = '\0';
+ syslog(LOG_DEBUG, _("Received random meta key (unencrypted): %s"), buffer);
+ }
+
+ /* All incoming requests will now be encrypted. */
+cp
+ /* Check and lookup cipher and digest algorithms */
+
+ if(cipher)
+ {
+ c->incipher = EVP_get_cipherbynid(cipher);
+ if(!c->incipher)
+ {
+ syslog(LOG_ERR, _("%s (%s) uses unknown cipher!"), c->name, c->hostname);
+ return -1;
+ }
+
+ EVP_DecryptInit(c->inctx, c->incipher,
+ c->inkey + len - c->incipher->key_len,
+ c->inkey + len - c->incipher->key_len - c->incipher->iv_len);
+
+ c->status.decryptin = 1;
+ }
+ else
+ {
+ c->incipher = NULL;
+ }
+
+ c->inmaclength = maclength;
+
+ if(digest)
+ {
+ c->indigest = EVP_get_digestbynid(digest);
+ if(!c->indigest)
+ {
+ syslog(LOG_ERR, _("Node %s (%s) uses unknown digest!"), c->name, c->hostname);
+ return -1;
+ }
+
+ if(c->inmaclength > c->indigest->md_size || c->inmaclength < 0)
+ {
+ syslog(LOG_ERR, _("%s (%s) uses bogus MAC length!"), c->name, c->hostname);
+ return -1;
+ }
+ }
+ else
+ {
+ c->indigest = NULL;
+ }
+
+ c->incompression = compression;
+
+ c->allow_request = CHALLENGE;
+cp
+ return send_challenge(c);
+}
+
+int send_challenge(connection_t *c)
+{
+ char buffer[MAX_STRING_SIZE];
+ int len, x;
+cp
+ /* CHECKME: what is most reasonable value for len? */
+
+ len = RSA_size(c->rsa_key);
+
+ /* Allocate buffers for the challenge */
+
+ if(!c->hischallenge)
+ c->hischallenge = xmalloc(len);
+cp
+ /* Copy random data to the buffer */
+
+ RAND_bytes(c->hischallenge, len);
+
+cp
+ /* Convert to hex */
+
+ bin2hex(c->hischallenge, buffer, len);
+ buffer[len*2] = '\0';
+
+cp
+ /* Send the challenge */
+
+ x = send_request(c, "%d %s", CHALLENGE, buffer);
+cp
+ return x;
+}
+
+int challenge_h(connection_t *c)
+{
+ char buffer[MAX_STRING_SIZE];
+ int len;
+cp
+ if(sscanf(c->buffer, "%*d "MAX_STRING, buffer) != 1)
+ {
+ syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "CHALLENGE", c->name, c->hostname);
+ return -1;
+ }
+
+ len = RSA_size(myself->connection->rsa_key);
+
+ /* Check if the length of the challenge is all right */
+
+ if(strlen(buffer) != len*2)
+ {
+ syslog(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, c->hostname, "wrong challenge length");
+ return -1;
+ }
+
+ /* Allocate buffers for the challenge */
+
+ if(!c->mychallenge)
+ c->mychallenge = xmalloc(len);
+
+ /* Convert the challenge from hexadecimal back to binary */
+
+ hex2bin(buffer,c->mychallenge,len);
+
+ c->allow_request = CHAL_REPLY;
+
+ /* Rest is done by send_chal_reply() */
+cp
+ return send_chal_reply(c);
+}
+
+int send_chal_reply(connection_t *c)
+{
+ char hash[EVP_MAX_MD_SIZE*2+1];
+ EVP_MD_CTX ctx;
+cp
+ /* Calculate the hash from the challenge we received */
+
+ EVP_DigestInit(&ctx, c->indigest);
+ EVP_DigestUpdate(&ctx, c->mychallenge, RSA_size(myself->connection->rsa_key));
+ EVP_DigestFinal(&ctx, hash, NULL);
+
+ /* Convert the hash to a hexadecimal formatted string */
+
+ bin2hex(hash,hash,c->indigest->md_size);
+ hash[c->indigest->md_size*2] = '\0';
+
+ /* Send the reply */
+
+cp
+ return send_request(c, "%d %s", CHAL_REPLY, hash);
+}
+
+int chal_reply_h(connection_t *c)
+{
+ char hishash[MAX_STRING_SIZE];
+ char myhash[EVP_MAX_MD_SIZE];
+ EVP_MD_CTX ctx;
+cp
+ if(sscanf(c->buffer, "%*d "MAX_STRING, hishash) != 1)
+ {
+ syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "CHAL_REPLY", c->name, c->hostname);
+ return -1;
+ }
+
+ /* Check if the length of the hash is all right */
+
+ if(strlen(hishash) != c->outdigest->md_size*2)
+ {
+ syslog(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, c->hostname, _("wrong challenge reply length"));
+ return -1;
+ }
+
+ /* Convert the hash to binary format */
+
+ hex2bin(hishash, hishash, c->outdigest->md_size);
+
+ /* Calculate the hash from the challenge we sent */
+
+ EVP_DigestInit(&ctx, c->outdigest);
+ EVP_DigestUpdate(&ctx, c->hischallenge, RSA_size(c->rsa_key));
+ EVP_DigestFinal(&ctx, myhash, NULL);
+
+ /* Verify the incoming hash with the calculated hash */
+
+ if(memcmp(hishash, myhash, c->outdigest->md_size))
+ {
+ syslog(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, c->hostname, _("wrong challenge reply"));
+ if(debug_lvl >= DEBUG_SCARY_THINGS)
+ {
+ bin2hex(myhash, hishash, SHA_DIGEST_LENGTH);
+ hishash[SHA_DIGEST_LENGTH*2] = '\0';
+ syslog(LOG_DEBUG, _("Expected challenge reply: %s"), hishash);
+ }
+ return -1;
+ }
+
+ /* Identity has now been positively verified.
+ Send an acknowledgement with the rest of the information needed.
+ */
+
+ c->allow_request = ACK;
+cp
+ return send_ack(c);
+}
+
+int send_ack(connection_t *c)
+{
+ /* ACK message contains rest of the information the other end needs
+ to create node_t and edge_t structures. */
+
+ int x;
+ char *address, *port;
+ struct timeval now;
+cp
+ /* Estimate weight */
+
+ gettimeofday(&now, NULL);
+ c->estimated_weight = (now.tv_sec - c->start.tv_sec) * 1000 + (now.tv_usec - c->start.tv_usec) / 1000;
+ sockaddr2str(&c->address, &address, &port);
+ x = send_request(c, "%d %s %s %d %lx", ACK, myport, address, c->estimated_weight, c->options);
+ free(address);
+ free(port);
+cp
+ return x;
+}
+
+void send_everything(connection_t *c)
+{
+ avl_node_t *node, *node2;
+ node_t *n;
+ subnet_t *s;
+ edge_t *e;
+
+ /* Send all known subnets */
+
+ for(node = node_tree->head; node; node = node->next)
+ {
+ n = (node_t *)node->data;
+
+ for(node2 = n->subnet_tree->head; node2; node2 = node2->next)
+ {
+ s = (subnet_t *)node2->data;
+ send_add_subnet(c, s);
+ }
+ }
+
+ /* Send all known edges */
+
+ for(node = edge_tree->head; node; node = node->next)
+ {
+ e = (edge_t *)node->data;
+
+ if(e == c->edge)
+ continue;
+
+ send_add_edge(c, e);
+ }
+}
+
+int ack_h(connection_t *c)
+{
+ char myaddress[MAX_STRING_SIZE];
+ char hisport[MAX_STRING_SIZE];
+ char *hisaddress, *dummy;
+ int weight;
+ long int options;
+ node_t *n;
+ connection_t *other;
+ avl_node_t *node;
+cp
+ if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING" %d %lx", hisport, myaddress, &weight, &options) != 4)
+ {
+ syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ACK", c->name, c->hostname);
+ return -1;
+ }
+
+ /* Check if we already have a node_t for him */
+
+ n = lookup_node(c->name);
+
+ if(!n)
+ {
+ n = new_node();
+ n->name = xstrdup(c->name);
+ node_add(n);
+ }
+ else
+ {
+ if(n->connection)
+ {
+ /* Oh dear, we already have a connection to this node. */
+ if(debug_lvl >= DEBUG_CONNECTIONS)
+ syslog(LOG_DEBUG, _("Established a second connection with %s (%s), closing old connection"), n->name, n->hostname);
+ terminate_connection(n->connection, 0);
+ }
+
+ /* FIXME: check if information in existing node matches that of the other end of this connection */
+ }
+
+ n->connection = c;
+ c->node = n;
+ c->options |= options;
+
+ /* Create an edge_t for this connection */
+
+ c->edge = new_edge();
+cp
+ c->edge->from.node = myself;
+ c->edge->from.udpaddress = str2sockaddr(myaddress, myport);
+ c->edge->to.node = n;
+ sockaddr2str(&c->address, &hisaddress, &dummy);
+ c->edge->to.udpaddress = str2sockaddr(hisaddress, hisport);
+ free(hisaddress);
+ free(dummy);
+ c->edge->weight = (weight + c->estimated_weight) / 2;
+ c->edge->connection = c;
+ c->edge->options = c->options;
+cp
+ edge_add(c->edge);
+
+ /* Activate this connection */
+
+ c->allow_request = ALL;
+ c->status.active = 1;
+
+ if(debug_lvl >= DEBUG_CONNECTIONS)
+ syslog(LOG_NOTICE, _("Connection with %s (%s) activated"), c->name, c->hostname);
+
+cp
+ /* Send him everything we know */
+
+ send_everything(c);
+
+ /* Notify others of this connection */
+
+ for(node = connection_tree->head; node; node = node->next)
+ {
+ other = (connection_t *)node->data;
+
+ if(other->status.active && other != c)
+ send_add_edge(other, c->edge);
+ }
+
+ /* Run MST and SSSP algorithms */
+
+ graph();
+cp
+ return 0;
+}
--- /dev/null
+/*
+ protocol_edge.c -- handle the meta-protocol, edges
+ Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
+ 2000-2002 Guus Sliepen <guus@sliepen.warande.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: protocol_edge.c,v 1.2 2002/04/09 15:26:00 zarq Exp $
+*/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include <utils.h>
+#include <xalloc.h>
+#include <avl_tree.h>
+
+#include "conf.h"
+#include "net.h"
+#include "netutl.h"
+#include "protocol.h"
+#include "meta.h"
+#include "connection.h"
+#include "node.h"
+#include "edge.h"
+#include "graph.h"
+
+#include "system.h"
+
+int send_add_edge(connection_t *c, edge_t *e)
+{
+ int x;
+ char *from_udpaddress, *from_udpport;
+ char *to_udpaddress, *to_udpport;
+cp
+ sockaddr2str(&e->from.udpaddress, &from_udpaddress, &from_udpport);
+ sockaddr2str(&e->to.udpaddress, &to_udpaddress, &to_udpport);
+ x = send_request(c, "%d %lx %s %s %s %s %s %s %lx %d", ADD_EDGE, random(),
+ e->from.node->name, from_udpaddress, from_udpport,
+ e->to.node->name, to_udpaddress, to_udpport,
+ e->options, e->weight);
+ free(from_udpaddress);
+ free(from_udpport);
+ free(to_udpaddress);
+ free(to_udpport);
+cp
+ return x;
+}
+
+int add_edge_h(connection_t *c)
+{
+ connection_t *other;
+ edge_t *e;
+ node_t *from, *to;
+ char from_name[MAX_STRING_SIZE];
+ char to_name[MAX_STRING_SIZE];
+ char from_address[MAX_STRING_SIZE];
+ char from_udpport[MAX_STRING_SIZE];
+ char to_address[MAX_STRING_SIZE];
+ char to_udpport[MAX_STRING_SIZE];
+ sockaddr_t from_udpaddress;
+ sockaddr_t to_udpaddress;
+ long int options;
+ int weight;
+ avl_node_t *node;
+cp
+ if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" %lx %d",
+ from_name, from_address, from_udpport,
+ to_name, to_address, to_udpport,
+ &options, &weight) != 8)
+ {
+ syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ADD_EDGE", c->name, c->hostname);
+ return -1;
+ }
+
+ /* Check if names are valid */
+
+ if(check_id(from_name))
+ {
+ syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_EDGE", c->name, c->hostname, _("invalid name"));
+ return -1;
+ }
+
+ if(check_id(to_name))
+ {
+ syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_EDGE", c->name, c->hostname, _("invalid name"));
+ return -1;
+ }
+
+ if(seen_request(c->buffer))
+ return 0;
+
+ /* Lookup nodes */
+
+ from = lookup_node(from_name);
+
+ if(!from)
+ {
+ from = new_node();
+ from->name = xstrdup(from_name);
+ node_add(from);
+ }
+
+ to = lookup_node(to_name);
+
+ if(!to)
+ {
+ to = new_node();
+ to->name = xstrdup(to_name);
+ node_add(to);
+ }
+
+ /* Convert addresses */
+
+ from_udpaddress = str2sockaddr(from_address, from_udpport);
+ to_udpaddress = str2sockaddr(to_address, to_udpport);
+
+ /* Check if edge already exists */
+
+ e = lookup_edge(from, to);
+
+ if(e)
+ {
+ if(e->weight != weight || e->options != options
+ || ((e->from.node == from) && (sockaddrcmp(&e->from.udpaddress, &from_udpaddress)|| sockaddrcmp(&e->to.udpaddress, &to_udpaddress)))
+ || ((e->from.node == to) && (sockaddrcmp(&e->from.udpaddress, &to_udpaddress) || sockaddrcmp(&e->to.udpaddress, &from_udpaddress)))
+ )
+ {
+ if(from == myself || to == myself)
+ {
+ if(debug_lvl >= DEBUG_PROTOCOL)
+ syslog(LOG_WARNING, _("Got %s from %s (%s) for ourself which does not match existing entry"), "ADD_EDGE", c->name, c->hostname);
+ send_add_edge(c, e);
+ return 0;
+ }
+ else
+ {
+ if(debug_lvl >= DEBUG_PROTOCOL)
+ syslog(LOG_WARNING, _("Got %s from %s (%s) which does not match existing entry"), "ADD_EDGE", c->name, c->hostname);
+ edge_del(e);
+ }
+ }
+ else
+ return 0;
+ }
+ else if(from == myself || to == myself)
+ {
+ if(debug_lvl >= DEBUG_PROTOCOL)
+ syslog(LOG_WARNING, _("Got %s from %s (%s) for ourself which does not exist"), "ADD_EDGE", c->name, c->hostname);
+ e = new_edge();
+ e->from.node = from;
+ e->to.node = to;
+ send_del_edge(c, e);
+ free_edge(e);
+ return 0;
+ }
+
+ e = new_edge();
+ e->from.node = from;
+ e->from.udpaddress = from_udpaddress;
+ e->to.node = to;
+ e->to.udpaddress = to_udpaddress;
+ e->options = options;
+ e->weight = weight;
+ edge_add(e);
+
+ /* Tell the rest about the new edge */
+
+ for(node = connection_tree->head; node; node = node->next)
+ {
+ other = (connection_t *)node->data;
+ if(other->status.active && other != c)
+ send_request(other, "%s", c->buffer);
+ }
+
+ /* Run MST before or after we tell the rest? */
+
+ graph();
+cp
+ return 0;
+}
+
+int send_del_edge(connection_t *c, edge_t *e)
+{
+cp
+ return send_request(c, "%d %lx %s %s", DEL_EDGE, random(),
+ e->from.node->name, e->to.node->name);
+}
+
+int del_edge_h(connection_t *c)
+{
+ edge_t *e;
+ char from_name[MAX_STRING_SIZE];
+ char to_name[MAX_STRING_SIZE];
+ node_t *from, *to;
+ connection_t *other;
+ avl_node_t *node;
+cp
+ if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING"", from_name, to_name) != 2)
+ {
+ syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "DEL_EDGE",
+ c->name, c->hostname);
+ return -1;
+ }
+
+ /* Check if names are valid */
+
+ if(check_id(from_name))
+ {
+ syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_EDGE", c->name, c->hostname, _("invalid name"));
+ return -1;
+ }
+
+ if(check_id(to_name))
+ {
+ syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_EDGE", c->name, c->hostname, _("invalid name"));
+ return -1;
+ }
+
+ if(seen_request(c->buffer))
+ return 0;
+
+ /* Lookup nodes */
+
+ from = lookup_node(from_name);
+
+ if(!from)
+ {
+ if(debug_lvl >= DEBUG_PROTOCOL)
+ syslog(LOG_ERR, _("Got %s from %s (%s) which does not appear in the edge tree"), "DEL_EDGE", c->name, c->hostname);
+ return 0;
+ }
+
+ to = lookup_node(to_name);
+
+ if(!to)
+ {
+ if(debug_lvl >= DEBUG_PROTOCOL)
+ syslog(LOG_ERR, _("Got %s from %s (%s) which does not appear in the edge tree"), "DEL_EDGE", c->name, c->hostname);
+ return 0;
+ }
+
+ /* Check if edge exists */
+
+ e = lookup_edge(from, to);
+
+ if(!e)
+ {
+ if(debug_lvl >= DEBUG_PROTOCOL)
+ syslog(LOG_WARNING, _("Got %s from %s (%s) which does not appear in the edge tree"), "DEL_EDGE", c->name, c->hostname);
+ return 0;
+ }
+
+ if(e->from.node == myself || e->to.node == myself)
+ {
+ if(debug_lvl >= DEBUG_PROTOCOL)
+ syslog(LOG_WARNING, _("Got %s from %s (%s) for ourself"), "DEL_EDGE", c->name, c->hostname);
+ send_add_edge(c, e); /* Send back a correction */
+ return 0;
+ }
+
+ /* Tell the rest about the deleted edge */
+
+ for(node = connection_tree->head; node; node = node->next)
+ {
+ other = (connection_t *)node->data;
+ if(other->status.active && other != c)
+ send_request(other, "%s", c->buffer);
+ }
+
+ /* Delete the edge */
+
+ edge_del(e);
+
+ /* Run MST before or after we tell the rest? */
+
+ graph();
+cp
+ return 0;
+}
--- /dev/null
+/*
+ protocol_key.c -- handle the meta-protocol, key exchange
+ Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
+ 2000-2002 Guus Sliepen <guus@sliepen.warande.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: protocol_key.c,v 1.2 2002/04/09 15:26:01 zarq Exp $
+*/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include <utils.h>
+#include <xalloc.h>
+#include <avl_tree.h>
+
+#include "conf.h"
+#include "net.h"
+#include "netutl.h"
+#include "protocol.h"
+#include "meta.h"
+#include "connection.h"
+#include "node.h"
+#include "edge.h"
+
+#include "system.h"
+
+int mykeyused = 0;
+
+int send_key_changed(connection_t *c, node_t *n)
+{
+ connection_t *other;
+ avl_node_t *node;
+cp
+ /* Only send this message if some other daemon requested our key previously.
+ This reduces unnecessary key_changed broadcasts.
+ */
+
+ if(n == myself && !mykeyused)
+ return 0;
+
+ for(node = connection_tree->head; node; node = node->next)
+ {
+ other = (connection_t *)node->data;
+ if(other->status.active && other != c)
+ send_request(other, "%d %lx %s", KEY_CHANGED, random(), n->name);
+ }
+cp
+ return 0;
+}
+
+int key_changed_h(connection_t *c)
+{
+ char name[MAX_STRING_SIZE];
+ avl_node_t *node;
+ connection_t *other;
+ node_t *n;
+cp
+ if(sscanf(c->buffer, "%*d %*x "MAX_STRING, name) != 1)
+ {
+ syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "KEY_CHANGED",
+ c->name, c->hostname);
+ return -1;
+ }
+
+ if(seen_request(c->buffer))
+ return 0;
+
+ n = lookup_node(name);
+
+ if(!n)
+ {
+ syslog(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist"), "KEY_CHANGED",
+ c->name, c->hostname, name);
+ return -1;
+ }
+
+ n->status.validkey = 0;
+ n->status.waitingforkey = 0;
+ n->sent_seqno = 0;
+
+ /* Tell the others */
+
+ for(node = connection_tree->head; node; node = node->next)
+ {
+ other = (connection_t *)node->data;
+ if(other->status.active && other != c)
+ send_request(other, "%s", c->buffer);
+ }
+cp
+ return 0;
+}
+
+int send_req_key(connection_t *c, node_t *from, node_t *to)
+{
+cp
+ return send_request(c, "%d %s %s", REQ_KEY,
+ from->name, to->name);
+}
+
+int req_key_h(connection_t *c)
+{
+ char from_name[MAX_STRING_SIZE];
+ char to_name[MAX_STRING_SIZE];
+ node_t *from, *to;
+cp
+ if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING, from_name, to_name) != 2)
+ {
+ syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "REQ_KEY",
+ c->name, c->hostname);
+ return -1;
+ }
+
+ from = lookup_node(from_name);
+
+ if(!from)
+ {
+ syslog(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist in our connection list"), "REQ_KEY",
+ c->name, c->hostname, from_name);
+ return -1;
+ }
+
+ to = lookup_node(to_name);
+
+ if(!to)
+ {
+ syslog(LOG_ERR, _("Got %s from %s (%s) destination %s which does not exist in our connection list"), "REQ_KEY",
+ c->name, c->hostname, to_name);
+ return -1;
+ }
+
+ /* Check if this key request is for us */
+
+ if(to == myself) /* Yes, send our own key back */
+ {
+ mykeyused = 1;
+ from->received_seqno = 0;
+ send_ans_key(c, myself, from);
+ }
+ else
+ {
+/* Proxy keys
+ if(to->status.validkey)
+ {
+ send_ans_key(c, to, from);
+ }
+ else
+*/
+ send_req_key(to->nexthop->connection, from, to);
+ }
+
+cp
+ return 0;
+}
+
+int send_ans_key(connection_t *c, node_t *from, node_t *to)
+{
+ char key[MAX_STRING_SIZE];
+cp
+ bin2hex(from->key, key, from->keylength);
+ key[from->keylength * 2] = '\0';
+cp
+ return send_request(c, "%d %s %s %s %d %d %d %d", ANS_KEY,
+ from->name, to->name, key, from->cipher?from->cipher->nid:0, from->digest?from->digest->type:0, from->maclength, from->compression);
+}
+
+int ans_key_h(connection_t *c)
+{
+ char from_name[MAX_STRING_SIZE];
+ char to_name[MAX_STRING_SIZE];
+ char key[MAX_STRING_SIZE];
+ int cipher, digest, maclength, compression;
+ node_t *from, *to;
+cp
+ if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d", from_name, to_name, key, &cipher, &digest, &maclength, &compression) != 7)
+ {
+ syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ANS_KEY",
+ c->name, c->hostname);
+ return -1;
+ }
+
+ from = lookup_node(from_name);
+
+ if(!from)
+ {
+ syslog(LOG_ERR, _("Got %s from %s (%s) origin %s which does not exist in our connection list"), "ANS_KEY",
+ c->name, c->hostname, from_name);
+ return -1;
+ }
+
+ to = lookup_node(to_name);
+
+ if(!to)
+ {
+ syslog(LOG_ERR, _("Got %s from %s (%s) destination %s which does not exist in our connection list"), "ANS_KEY",
+ c->name, c->hostname, to_name);
+ return -1;
+ }
+
+ /* Forward it if necessary */
+
+ if(to != myself)
+ {
+ return send_request(to->nexthop->connection, "%s", c->buffer);
+ }
+
+ /* Update our copy of the origin's packet key */
+
+ if(from->key)
+ free(from->key);
+
+ from->key = xstrdup(key);
+ from->keylength = strlen(key) / 2;
+ hex2bin(from->key, from->key, from->keylength);
+ from->key[from->keylength] = '\0';
+
+ from->status.validkey = 1;
+ from->status.waitingforkey = 0;
+
+ /* Check and lookup cipher and digest algorithms */
+
+ if(cipher)
+ {
+ from->cipher = EVP_get_cipherbynid(cipher);
+ if(!from->cipher)
+ {
+ syslog(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name, from->hostname);
+ return -1;
+ }
+ if(from->keylength != from->cipher->key_len + from->cipher->iv_len)
+ {
+ syslog(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name, from->hostname);
+ return -1;
+ }
+ }
+ else
+ {
+ from->cipher = NULL;
+ }
+
+ from->maclength = maclength;
+
+ if(digest)
+ {
+ from->digest = EVP_get_digestbynid(digest);
+ if(!from->digest)
+ {
+ syslog(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name, from->hostname);
+ return -1;
+ }
+ if(from->maclength > from->digest->md_size || from->maclength < 0)
+ {
+ syslog(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"), from->name, from->hostname);
+ return -1;
+ }
+ }
+ else
+ {
+ from->digest = NULL;
+ }
+
+ from->compression = compression;
+
+ flush_queue(from);
+cp
+ return 0;
+}
--- /dev/null
+/*
+ protocol_misc.c -- handle the meta-protocol, miscellaneous functions
+ Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
+ 2000-2002 Guus Sliepen <guus@sliepen.warande.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: protocol_misc.c,v 1.2 2002/04/09 15:26:01 zarq Exp $
+*/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include <utils.h>
+
+#include "conf.h"
+#include "net.h"
+#include "netutl.h"
+#include "protocol.h"
+#include "meta.h"
+#include "connection.h"
+
+#include "system.h"
+
+/* Status and error notification routines */
+
+int send_status(connection_t *c, int statusno, char *statusstring)
+{
+cp
+ if(!statusstring)
+ statusstring = status_text[statusno];
+cp
+ return send_request(c, "%d %d %s", STATUS, statusno, statusstring);
+}
+
+int status_h(connection_t *c)
+{
+ int statusno;
+ char statusstring[MAX_STRING_SIZE];
+cp
+ if(sscanf(c->buffer, "%*d %d "MAX_STRING, &statusno, statusstring) != 2)
+ {
+ syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "STATUS",
+ c->name, c->hostname);
+ return -1;
+ }
+
+ if(debug_lvl >= DEBUG_STATUS)
+ {
+ syslog(LOG_NOTICE, _("Status message from %s (%s): %s: %s"),
+ c->name, c->hostname, status_text[statusno], statusstring);
+ }
+
+cp
+ return 0;
+}
+
+int send_error(connection_t *c, int err, char *errstring)
+{
+cp
+ if(!errstring)
+ errstring = strerror(err);
+ return send_request(c, "%d %d %s", ERROR, err, errstring);
+}
+
+int error_h(connection_t *c)
+{
+ int err;
+ char errorstring[MAX_STRING_SIZE];
+cp
+ if(sscanf(c->buffer, "%*d %d "MAX_STRING, &err, errorstring) != 2)
+ {
+ syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ERROR",
+ c->name, c->hostname);
+ return -1;
+ }
+
+ if(debug_lvl >= DEBUG_ERROR)
+ {
+ syslog(LOG_NOTICE, _("Error message from %s (%s): %s: %s"),
+ c->name, c->hostname, strerror(err), errorstring);
+ }
+
+ terminate_connection(c, c->status.active);
+cp
+ return 0;
+}
+
+int send_termreq(connection_t *c)
+{
+cp
+ return send_request(c, "%d", TERMREQ);
+}
+
+int termreq_h(connection_t *c)
+{
+cp
+ terminate_connection(c, c->status.active);
+cp
+ return 0;
+}
+
+int send_ping(connection_t *c)
+{
+cp
+ c->status.pinged = 1;
+ c->last_ping_time = now;
+cp
+ return send_request(c, "%d", PING);
+}
+
+int ping_h(connection_t *c)
+{
+cp
+ return send_pong(c);
+}
+
+int send_pong(connection_t *c)
+{
+cp
+ return send_request(c, "%d", PONG);
+}
+
+int pong_h(connection_t *c)
+{
+cp
+ c->status.pinged = 0;
+
+ /* Succesful connection, reset timeout if this is an outgoing connection. */
+
+ if(c->outgoing)
+ c->outgoing->timeout = 0;
+cp
+ return 0;
+}
+
+/* Sending and receiving packets via TCP */
+
+int send_tcppacket(connection_t *c, vpn_packet_t *packet)
+{
+ int x;
+cp
+ /* Evil hack. */
+
+ x = send_request(c, "%d %hd", PACKET, packet->len);
+
+ if(x)
+ return x;
+cp
+ return send_meta(c, packet->data, packet->len);
+}
+
+int tcppacket_h(connection_t *c)
+{
+ short int len;
+cp
+ if(sscanf(c->buffer, "%*d %hd", &len) != 1)
+ {
+ syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "PACKET", c->name, c->hostname);
+ return -1;
+ }
+
+ /* Set reqlen to len, this will tell receive_meta() that a tcppacket is coming. */
+
+ c->tcplen = len;
+cp
+ return 0;
+}
+
+/* Status strings */
+
+char (*status_text[]) = {
+ "Warning",
+};
+
+/* Error strings */
+
+char (*error_text[]) = {
+ "Error",
+};
--- /dev/null
+/*
+ protocol_subnet.c -- handle the meta-protocol, subnets
+ Copyright (C) 1999-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
+ 2000-2002 Guus Sliepen <guus@sliepen.warande.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: protocol_subnet.c,v 1.2 2002/04/09 15:26:01 zarq Exp $
+*/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include <utils.h>
+#include <xalloc.h>
+#include <avl_tree.h>
+
+#include "conf.h"
+#include "net.h"
+#include "netutl.h"
+#include "protocol.h"
+#include "meta.h"
+#include "connection.h"
+#include "node.h"
+#include "edge.h"
+#include "graph.h"
+
+#include "system.h"
+
+int send_add_subnet(connection_t *c, subnet_t *subnet)
+{
+ int x;
+ char *netstr;
+cp
+ x = send_request(c, "%d %lx %s %s", ADD_SUBNET, random(),
+ subnet->owner->name, netstr = net2str(subnet));
+ free(netstr);
+cp
+ return x;
+}
+
+int add_subnet_h(connection_t *c)
+{
+ char subnetstr[MAX_STRING_SIZE];
+ char name[MAX_STRING_SIZE];
+ node_t *owner;
+ connection_t *other;
+ subnet_t *s;
+ avl_node_t *node;
+cp
+ if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING, name, subnetstr) != 2)
+ {
+ syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ADD_SUBNET", c->name, c->hostname);
+ return -1;
+ }
+
+ /* Check if owner name is a valid */
+
+ if(check_id(name))
+ {
+ syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_SUBNET", c->name, c->hostname, _("invalid name"));
+ return -1;
+ }
+
+ /* Check if subnet string is valid */
+
+ if(!(s = str2net(subnetstr)))
+ {
+ syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_SUBNET", c->name, c->hostname, _("invalid subnet string"));
+ return -1;
+ }
+
+ if(seen_request(c->buffer))
+ return 0;
+
+ /* Check if the owner of the new subnet is in the connection list */
+
+ owner = lookup_node(name);
+
+ if(!owner)
+ {
+ owner = new_node();
+ owner->name = xstrdup(name);
+ node_add(owner);
+ }
+
+ /* Check if we already know this subnet */
+
+ if(lookup_subnet(owner, s))
+ {
+ free_subnet(s);
+ return 0;
+ }
+
+ /* If we don't know this subnet, but we are the owner, retaliate with a DEL_SUBNET */
+
+ if(owner == myself)
+ {
+ if(debug_lvl >= DEBUG_PROTOCOL)
+ syslog(LOG_WARNING, _("Got %s from %s (%s) for ourself"), "ADD_SUBNET", c->name, c->hostname);
+ s->owner = myself;
+ send_del_subnet(c, s);
+ return 0;
+ }
+
+ /* If everything is correct, add the subnet to the list of the owner */
+
+ subnet_add(owner, s);
+
+ /* Tell the rest */
+
+ for(node = connection_tree->head; node; node = node->next)
+ {
+ other = (connection_t *)node->data;
+ if(other->status.active && other != c)
+ send_request(other, "%s", c->buffer);
+ }
+cp
+ return 0;
+}
+
+int send_del_subnet(connection_t *c, subnet_t *s)
+{
+ int x;
+ char *netstr;
+cp
+ netstr = net2str(s);
+ x = send_request(c, "%d %lx %s %s", DEL_SUBNET, random(), s->owner->name, netstr);
+ free(netstr);
+cp
+ return x;
+}
+
+int del_subnet_h(connection_t *c)
+{
+ char subnetstr[MAX_STRING_SIZE];
+ char name[MAX_STRING_SIZE];
+ node_t *owner;
+ connection_t *other;
+ subnet_t *s, *find;
+ avl_node_t *node;
+cp
+ if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING, name, subnetstr) != 2)
+ {
+ syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "DEL_SUBNET", c->name, c->hostname);
+ return -1;
+ }
+
+ /* Check if owner name is a valid */
+
+ if(check_id(name))
+ {
+ syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_SUBNET", c->name, c->hostname, _("invalid name"));
+ return -1;
+ }
+
+ /* Check if the owner of the new subnet is in the connection list */
+
+ if(!(owner = lookup_node(name)))
+ {
+ if(debug_lvl >= DEBUG_PROTOCOL)
+ syslog(LOG_WARNING, _("Got %s from %s (%s) for %s which is not in our node tree"),
+ "DEL_SUBNET", c->name, c->hostname, name);
+ return 0;
+ }
+
+ /* Check if subnet string is valid */
+
+ if(!(s = str2net(subnetstr)))
+ {
+ syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_SUBNET", c->name, c->hostname, _("invalid subnet string"));
+ return -1;
+ }
+
+ if(seen_request(c->buffer))
+ return 0;
+
+ /* If everything is correct, delete the subnet from the list of the owner */
+
+ s->owner = owner;
+
+ find = lookup_subnet(owner, s);
+
+ free_subnet(s);
+
+ if(!find)
+ {
+ if(debug_lvl >= DEBUG_PROTOCOL)
+ syslog(LOG_WARNING, _("Got %s from %s (%s) for %s which does not appear in his subnet tree"),
+ "DEL_SUBNET", c->name, c->hostname, name);
+ return 0;
+ }
+
+ /* If we are the owner of this subnet, retaliate with an ADD_SUBNET */
+
+ if(owner == myself)
+ {
+ if(debug_lvl >= DEBUG_PROTOCOL)
+ syslog(LOG_WARNING, _("Got %s from %s (%s) for ourself"), "DEL_SUBNET", c->name, c->hostname);
+ send_add_subnet(c, find);
+ return 0;
+ }
+
+ /* Tell the rest */
+
+ for(node = connection_tree->head; node; node = node->next)
+ {
+ other = (connection_t *)node->data;
+ if(other->status.active && other != c)
+ send_request(other, "%s", c->buffer);
+ }
+
+ /* Finally, delete it. */
+
+ subnet_del(owner, find);
+
+cp
+ return 0;
+}
--- /dev/null
+/*
+ route.c -- routing
+ Copyright (C) 2000-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
+ 2000-2002 Guus Sliepen <guus@sliepen.warande.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: route.c,v 1.2 2002/04/09 15:26:01 zarq Exp $
+*/
+
+#include "config.h"
+
+#if defined(HAVE_FREEBSD) || defined(HAVE_OPENBSD)
+ #include <sys/param.h>
+#endif
+#include <sys/socket.h>
+#include <netinet/in.h>
+#if defined(HAVE_SOLARIS) || defined(HAVE_OPENBSD)
+ #include <net/if.h>
+ #define ETHER_ADDR_LEN 6
+#else
+ #include <net/ethernet.h>
+#endif
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <netinet/if_ether.h>
+#include <utils.h>
+#include <xalloc.h>
+#include <syslog.h>
+#include <string.h>
+
+#include <avl_tree.h>
+
+#include "net.h"
+#include "connection.h"
+#include "subnet.h"
+#include "route.h"
+#include "protocol.h"
+#include "device.h"
+
+#include "system.h"
+
+int routing_mode = RMODE_ROUTER;
+int priorityinheritance = 0;
+int macexpire = 600;
+subnet_t mymac;
+
+void learn_mac(mac_t *address)
+{
+ subnet_t *subnet;
+ avl_node_t *node;
+ connection_t *c;
+cp
+ subnet = lookup_subnet_mac(address);
+
+ /* If we don't know this MAC address yet, store it */
+
+ if(!subnet || subnet->owner!=myself)
+ {
+ if(debug_lvl >= DEBUG_TRAFFIC)
+ syslog(LOG_INFO, _("Learned new MAC address %hx:%hx:%hx:%hx:%hx:%hx"),
+ address->x[0], address->x[1], address->x[2], address->x[3], address->x[4], address->x[5]);
+
+ subnet = new_subnet();
+ subnet->type = SUBNET_MAC;
+ memcpy(&subnet->net.mac.address, address, sizeof(mac_t));
+ subnet_add(myself, subnet);
+
+ /* And tell all other tinc daemons it's our MAC */
+
+ for(node = connection_tree->head; node; node = node->next)
+ {
+ c = (connection_t *)node->data;
+ if(c->status.active)
+ send_add_subnet(c, subnet);
+ }
+ }
+
+ subnet->net.mac.lastseen = now;
+}
+
+void age_mac(void)
+{
+ subnet_t *s;
+ connection_t *c;
+ avl_node_t *node, *next, *node2;
+cp
+ for(node = myself->subnet_tree->head; node; node = next)
+ {
+ next = node->next;
+ s = (subnet_t *)node->data;
+ if(s->type == SUBNET_MAC && s->net.mac.lastseen && s->net.mac.lastseen + macexpire < now)
+ {
+ if(debug_lvl >= DEBUG_TRAFFIC)
+ syslog(LOG_INFO, _("MAC address %hx:%hx:%hx:%hx:%hx:%hx expired"),
+ s->net.mac.address.x[0], s->net.mac.address.x[1], s->net.mac.address.x[2], s->net.mac.address.x[3], s->net.mac.address.x[4], s->net.mac.address.x[5]);
+ for(node2 = connection_tree->head; node2; node2 = node2->next)
+ {
+ c = (connection_t *)node2->data;
+ if(c->status.active)
+ send_del_subnet(c, s);
+ }
+ subnet_del(myself, s);
+ }
+ }
+cp
+}
+
+node_t *route_mac(vpn_packet_t *packet)
+{
+ subnet_t *subnet;
+cp
+ /* Learn source address */
+
+ learn_mac((mac_t *)(&packet->data[6]));
+
+ /* Lookup destination address */
+
+ subnet = lookup_subnet_mac((mac_t *)(&packet->data[0]));
+
+ if(subnet)
+ return subnet->owner;
+ else
+ return NULL;
+}
+
+node_t *route_ipv4(vpn_packet_t *packet)
+{
+ subnet_t *subnet;
+cp
+ if(priorityinheritance)
+ packet->priority = packet->data[15];
+
+ subnet = lookup_subnet_ipv4((ipv4_t *)&packet->data[30]);
+cp
+ if(!subnet)
+ {
+ if(debug_lvl >= DEBUG_TRAFFIC)
+ {
+ syslog(LOG_WARNING, _("Cannot route packet: unknown IPv4 destination address %d.%d.%d.%d"),
+ packet->data[30], packet->data[31], packet->data[32], packet->data[33]);
+ }
+
+ return NULL;
+ }
+cp
+ return subnet->owner;
+}
+
+node_t *route_ipv6(vpn_packet_t *packet)
+{
+ subnet_t *subnet;
+cp
+ subnet = lookup_subnet_ipv6((ipv6_t *)&packet->data[38]);
+cp
+ if(!subnet)
+ {
+ if(debug_lvl >= DEBUG_TRAFFIC)
+ {
+ syslog(LOG_WARNING, _("Cannot route packet: unknown IPv6 destination address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx"),
+ ntohs(*(short unsigned int *)&packet->data[38]),
+ ntohs(*(short unsigned int *)&packet->data[40]),
+ ntohs(*(short unsigned int *)&packet->data[42]),
+ ntohs(*(short unsigned int *)&packet->data[44]),
+ ntohs(*(short unsigned int *)&packet->data[46]),
+ ntohs(*(short unsigned int *)&packet->data[48]),
+ ntohs(*(short unsigned int *)&packet->data[50]),
+ ntohs(*(short unsigned int *)&packet->data[52]));
+ }
+
+ return NULL;
+ }
+cp
+ return subnet->owner;
+}
+
+unsigned short int inet_checksum(unsigned short int *data, int len, unsigned short int prevsum)
+{
+ unsigned long int checksum = prevsum ^ 0xFFFF;
+
+ while(len--)
+ checksum += ntohs(*data++);
+
+ while(checksum >> 16)
+ checksum = (checksum & 0xFFFF) + (checksum >> 16);
+
+ return checksum ^ 0xFFFF;
+}
+
+void route_neighborsol(vpn_packet_t *packet)
+{
+ struct ip6_hdr *hdr;
+ struct nd_neighbor_solicit *ns;
+ struct nd_opt_hdr *opt;
+ subnet_t *subnet;
+ short unsigned int checksum;
+
+ struct {
+ struct in6_addr ip6_src; /* source address */
+ struct in6_addr ip6_dst; /* destination address */
+ uint32_t length;
+ uint8_t junk[4];
+ } pseudo;
+
+cp
+ hdr = (struct ip6_hdr *)(packet->data + 14);
+ ns = (struct nd_neighbor_solicit *)(packet->data + 14 + sizeof(*hdr));
+ opt = (struct nd_opt_hdr *)(packet->data + 14 + sizeof(*hdr) + sizeof(*ns));
+
+ /* First, snatch the source address from the neighbor solicitation packet */
+
+ memcpy(mymac.net.mac.address.x, packet->data + 6, 6);
+
+ /* Check if this is a valid neighbor solicitation request */
+
+ if(ns->nd_ns_hdr.icmp6_type != ND_NEIGHBOR_SOLICIT ||
+ opt->nd_opt_type != ND_OPT_SOURCE_LINKADDR)
+ {
+ if(debug_lvl > DEBUG_TRAFFIC)
+ {
+ syslog(LOG_WARNING, _("Cannot route packet: received unknown type neighbor solicitation request"));
+ }
+ return;
+ }
+
+ /* Create pseudo header */
+
+ memcpy(&pseudo.ip6_src, &hdr->ip6_src, 16);
+ memcpy(&pseudo.ip6_dst, &hdr->ip6_dst, 16);
+ pseudo.length = htonl(sizeof(*ns) + sizeof(*opt) + 6);
+ pseudo.junk[0] = pseudo.junk[1] = pseudo.junk[2] = 0;
+ pseudo.junk[3] = IPPROTO_ICMPV6;
+
+ /* Generate checksum */
+
+ checksum = inet_checksum((unsigned short int *)&pseudo, sizeof(pseudo)/2, ~0);
+ checksum = inet_checksum((unsigned short int *)ns, sizeof(*ns)/2 + 4, checksum);
+
+ if(checksum)
+ {
+ if(debug_lvl >= DEBUG_TRAFFIC)
+ syslog(LOG_WARNING, _("Cannot route packet: checksum error for neighbor solicitation request"));
+ return;
+ }
+
+ /* Check if the IPv6 address exists on the VPN */
+
+ subnet = lookup_subnet_ipv6((ipv6_t *)&ns->nd_ns_target);
+
+ if(!subnet)
+ {
+ if(debug_lvl >= DEBUG_TRAFFIC)
+ {
+ syslog(LOG_WARNING, _("Cannot route packet: neighbor solicitation request for unknown address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx"),
+ ntohs(((uint16_t *)&ns->nd_ns_target)[0]), ntohs(((uint16_t *)&ns->nd_ns_target)[1]), ntohs(((uint16_t *)&ns->nd_ns_target)[2]), ntohs(((uint16_t *)&ns->nd_ns_target)[3]),
+ ntohs(((uint16_t *)&ns->nd_ns_target)[4]), ntohs(((uint16_t *)&ns->nd_ns_target)[5]), ntohs(((uint16_t *)&ns->nd_ns_target)[6]), ntohs(((uint16_t *)&ns->nd_ns_target)[7]));
+ }
+
+ return;
+ }
+
+ /* Check if it is for our own subnet */
+
+ if(subnet->owner == myself)
+ return; /* silently ignore */
+
+ /* Create neighbor advertation reply */
+
+ memcpy(packet->data, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN); /* copy destination address */
+ packet->data[ETHER_ADDR_LEN*2 - 1] ^= 0xFF; /* mangle source address so it looks like it's not from us */
+
+ memcpy(&hdr->ip6_dst, &hdr->ip6_src, 16); /* swap destination and source protocol address */
+ memcpy(&hdr->ip6_src, &ns->nd_ns_target, 16); /* ... */
+
+ memcpy((char *)opt + sizeof(*opt), packet->data + ETHER_ADDR_LEN, 6); /* add fake source hard addr */
+
+ ns->nd_ns_hdr.icmp6_cksum = 0;
+ ns->nd_ns_hdr.icmp6_type = ND_NEIGHBOR_ADVERT;
+ ns->nd_ns_hdr.icmp6_dataun.icmp6_un_data8[0] = 0x40; /* Set solicited flag */
+ ns->nd_ns_hdr.icmp6_dataun.icmp6_un_data8[1] = ns->nd_ns_hdr.icmp6_dataun.icmp6_un_data8[2] = ns->nd_ns_hdr.icmp6_dataun.icmp6_un_data8[3] = 0;
+ opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
+
+ /* Create pseudo header */
+
+ memcpy(&pseudo.ip6_src, &hdr->ip6_src, 16);
+ memcpy(&pseudo.ip6_dst, &hdr->ip6_dst, 16);
+ pseudo.length = htonl(sizeof(*ns) + sizeof(*opt) + 6);
+ pseudo.junk[0] = pseudo.junk[1] = pseudo.junk[2] = 0;
+ pseudo.junk[3] = IPPROTO_ICMPV6;
+
+ /* Generate checksum */
+
+ checksum = inet_checksum((unsigned short int *)&pseudo, sizeof(pseudo)/2, ~0);
+ checksum = inet_checksum((unsigned short int *)ns, sizeof(*ns)/2 + 4, checksum);
+
+ ns->nd_ns_hdr.icmp6_cksum = htons(checksum);
+
+ write_packet(packet);
+cp
+}
+
+void route_arp(vpn_packet_t *packet)
+{
+ struct ether_arp *arp;
+ subnet_t *subnet;
+ unsigned char ipbuf[4];
+cp
+ /* First, snatch the source address from the ARP packet */
+
+ memcpy(mymac.net.mac.address.x, packet->data + 6, 6);
+
+ /* This routine generates replies to ARP requests.
+ You don't need to set NOARP flag on the interface anymore (which is broken on FreeBSD).
+ Most of the code here is taken from choparp.c by Takamichi Tateoka (tree@mma.club.uec.ac.jp)
+ */
+
+ arp = (struct ether_arp *)(packet->data + 14);
+
+ /* Check if this is a valid ARP request */
+
+ if(ntohs(arp->arp_hrd) != ARPHRD_ETHER ||
+ ntohs(arp->arp_pro) != ETHERTYPE_IP ||
+ (int) (arp->arp_hln) != ETHER_ADDR_LEN ||
+ (int) (arp->arp_pln) != 4 ||
+ ntohs(arp->arp_op) != ARPOP_REQUEST )
+ {
+ if(debug_lvl > DEBUG_TRAFFIC)
+ {
+ syslog(LOG_WARNING, _("Cannot route packet: received unknown type ARP request"));
+ }
+ return;
+ }
+
+ /* Check if the IPv4 address exists on the VPN */
+
+ subnet = lookup_subnet_ipv4((ipv4_t *)arp->arp_tpa);
+
+ if(!subnet)
+ {
+ if(debug_lvl >= DEBUG_TRAFFIC)
+ {
+ syslog(LOG_WARNING, _("Cannot route packet: ARP request for unknown address %d.%d.%d.%d"),
+ arp->arp_tpa[0], arp->arp_tpa[1], arp->arp_tpa[2], arp->arp_tpa[3]);
+ }
+
+ return;
+ }
+
+ /* Check if it is for our own subnet */
+
+ if(subnet->owner == myself)
+ return; /* silently ignore */
+
+ memcpy(packet->data, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN); /* copy destination address */
+ packet->data[ETHER_ADDR_LEN*2 - 1] ^= 0xFF; /* mangle source address so it looks like it's not from us */
+
+ memcpy(ipbuf, arp->arp_tpa, 4); /* save protocol addr */
+ memcpy(arp->arp_tpa, arp->arp_spa, 4); /* swap destination and source protocol address */
+ memcpy(arp->arp_spa, ipbuf, 4); /* ... */
+
+ memcpy(arp->arp_tha, arp->arp_sha, 10); /* set target hard/proto addr */
+ memcpy(arp->arp_sha, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN); /* add fake source hard addr */
+ arp->arp_op = htons(ARPOP_REPLY);
+
+ write_packet(packet);
+cp
+}
+
+void route_outgoing(vpn_packet_t *packet)
+{
+ unsigned short int type;
+ node_t *n = NULL;
+cp
+ /* FIXME: multicast? */
+
+ switch(routing_mode)
+ {
+ case RMODE_ROUTER:
+ type = ntohs(*((unsigned short*)(&packet->data[12])));
+ switch(type)
+ {
+ case 0x0800:
+ n = route_ipv4(packet);
+ break;
+ case 0x86DD:
+ if(packet->data[20] == IPPROTO_ICMPV6 && packet->data[54] == ND_NEIGHBOR_SOLICIT)
+ {
+ route_neighborsol(packet);
+ return;
+ }
+ n = route_ipv6(packet);
+ break;
+ case 0x0806:
+ route_arp(packet);
+ return;
+ default:
+ if(debug_lvl >= DEBUG_TRAFFIC)
+ {
+ syslog(LOG_WARNING, _("Cannot route packet: unknown type %hx"), type);
+ }
+ return;
+ }
+ if(n)
+ send_packet(n, packet);
+ break;
+
+ case RMODE_SWITCH:
+ n = route_mac(packet);
+ if(n)
+ send_packet(n, packet);
+ else
+ broadcast_packet(myself, packet);
+ break;
+
+ case RMODE_HUB:
+ broadcast_packet(myself, packet);
+ break;
+ }
+}
+
+void route_incoming(node_t *source, vpn_packet_t *packet)
+{
+ switch(routing_mode)
+ {
+ case RMODE_ROUTER:
+ {
+ node_t *n = NULL;
+ unsigned short int type;
+
+ type = ntohs(*((unsigned short*)(&packet->data[12])));
+ switch(type)
+ {
+ case 0x0800:
+ n = route_ipv4(packet);
+ break;
+ case 0x86DD:
+ n = route_ipv6(packet);
+ break;
+ default:
+ n = myself;
+ break;
+ }
+
+ if(n)
+ {
+ if(n == myself)
+ {
+ memcpy(packet->data, mymac.net.mac.address.x, 6);
+ write_packet(packet);
+ }
+ else
+ send_packet(n, packet);
+ }
+ }
+ break;
+ case RMODE_SWITCH:
+ {
+ subnet_t *subnet;
+
+ subnet = lookup_subnet_mac((mac_t *)(&packet->data[0]));
+
+ if(subnet)
+ {
+ if(subnet->owner == myself)
+ write_packet(packet);
+ else
+ send_packet(subnet->owner, packet);
+ }
+ else
+ {
+ broadcast_packet(source, packet);
+ write_packet(packet);
+ }
+ }
+ break;
+ case RMODE_HUB:
+ broadcast_packet(source, packet); /* Spread it on */
+ write_packet(packet);
+ break;
+ }
+}
--- /dev/null
+/*
+ route.h -- header file for route.c
+ Copyright (C) 2000-2002 Ivo Timmermans <zarq@iname.com>
+ 2000-2002 Guus Sliepen <guus@sliepen.warande.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: route.h,v 1.2 2002/04/09 15:26:01 zarq Exp $
+*/
+
+#ifndef __TINC_ROUTE_H__
+#define __TINC_ROUTE_H__
+
+enum
+{
+ RMODE_HUB = 0,
+ RMODE_SWITCH,
+ RMODE_ROUTER,
+};
+
+extern int routing_mode;
+extern int priorityinheritance;
+extern int macexpire;
+
+extern void age_mac(void);
+extern void route_incoming(node_t *, vpn_packet_t *);
+extern void route_outgoing(vpn_packet_t *);
+
+#endif /* __TINC_ROUTE_H__ */
--- /dev/null
+/*
+ subnet.c -- handle subnet lookups and lists
+ Copyright (C) 2000-2002 Guus Sliepen <guus@sliepen.warande.net>,
+ 2000-2002 Ivo Timmermans <itimmermans@bigfoot.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: subnet.c,v 1.2 2002/04/09 15:26:01 zarq Exp $
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+
+#include <utils.h>
+#include <xalloc.h>
+#include <avl_tree.h>
+
+#include "conf.h"
+#include "net.h"
+#include "node.h"
+#include "subnet.h"
+#include "netutl.h"
+
+#include "system.h"
+
+/* lists type of subnet */
+
+avl_tree_t *subnet_tree;
+
+/* Subnet comparison */
+
+int subnet_compare_mac(subnet_t *a, subnet_t *b)
+{
+cp
+ return memcmp(&a->net.mac.address, &b->net.mac.address, sizeof(mac_t));
+}
+
+int subnet_compare_ipv4(subnet_t *a, subnet_t *b)
+{
+ int result;
+cp
+ result = memcmp(&a->net.ipv4.address, &b->net.ipv4.address, sizeof(ipv4_t));
+
+ if(result)
+ return result;
+
+ return a->net.ipv4.prefixlength - b->net.ipv4.prefixlength;
+}
+
+int subnet_compare_ipv6(subnet_t *a, subnet_t *b)
+{
+ int result;
+cp
+ result = memcmp(&a->net.ipv6.address, &b->net.ipv6.address, sizeof(ipv6_t));
+
+ if(result)
+ return result;
+
+ return a->net.ipv6.prefixlength - b->net.ipv6.prefixlength;
+}
+
+int subnet_compare(subnet_t *a, subnet_t *b)
+{
+ int result;
+cp
+ result = a->type - b->type;
+
+ if(result)
+ return result;
+
+ switch(a->type)
+ {
+ case SUBNET_MAC:
+ return subnet_compare_mac(a, b);
+ case SUBNET_IPV4:
+ return subnet_compare_ipv4(a, b);
+ case SUBNET_IPV6:
+ return subnet_compare_ipv6(a, b);
+ default:
+ syslog(LOG_ERR, _("subnet_compare() was called with unknown subnet type %d, exitting!"), a->type);
+ cp_trace();
+ exit(0);
+ }
+
+ return 0;
+}
+
+/* Initialising trees */
+
+void init_subnets(void)
+{
+cp
+ subnet_tree = avl_alloc_tree((avl_compare_t)subnet_compare, (avl_action_t)free_subnet);
+cp
+}
+
+void exit_subnets(void)
+{
+cp
+ avl_delete_tree(subnet_tree);
+cp
+}
+
+avl_tree_t *new_subnet_tree(void)
+{
+cp
+ return avl_alloc_tree((avl_compare_t)subnet_compare, NULL);
+cp
+}
+
+void free_subnet_tree(avl_tree_t *subnet_tree)
+{
+cp
+ avl_delete_tree(subnet_tree);
+cp
+}
+
+/* Allocating and freeing space for subnets */
+
+subnet_t *new_subnet(void)
+{
+cp
+ return (subnet_t *)xmalloc(sizeof(subnet_t));
+}
+
+void free_subnet(subnet_t *subnet)
+{
+cp
+ free(subnet);
+}
+
+/* Adding and removing subnets */
+
+void subnet_add(node_t *n, subnet_t *subnet)
+{
+cp
+ subnet->owner = n;
+
+ avl_insert(subnet_tree, subnet);
+cp
+ avl_insert(n->subnet_tree, subnet);
+cp
+}
+
+void subnet_del(node_t *n, subnet_t *subnet)
+{
+cp
+ avl_delete(n->subnet_tree, subnet);
+cp
+ avl_delete(subnet_tree, subnet);
+cp
+}
+
+/* Ascii representation of subnets */
+
+subnet_t *str2net(char *subnetstr)
+{
+ int i, l;
+ subnet_t *subnet;
+ unsigned short int x[8];
+cp
+ subnet = new_subnet();
+cp
+ if(sscanf(subnetstr, "%hu.%hu.%hu.%hu/%d",
+ &x[0], &x[1], &x[2], &x[3],
+ &l) == 5)
+ {
+ subnet->type = SUBNET_IPV4;
+ subnet->net.ipv4.prefixlength = l;
+ for(i = 0; i < 4; i++)
+ subnet->net.ipv4.address.x[i] = x[i];
+ return subnet;
+ }
+
+ if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d",
+ &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7],
+ &l) == 9)
+ {
+ subnet->type = SUBNET_IPV6;
+ subnet->net.ipv6.prefixlength = l;
+ for(i = 0; i < 8; i++)
+ subnet->net.ipv6.address.x[i] = htons(x[i]);
+ return subnet;
+ }
+
+ if(sscanf(subnetstr, "%hu.%hu.%hu.%hu",
+ &x[0], &x[1], &x[2], &x[3]) == 4)
+ {
+ subnet->type = SUBNET_IPV4;
+ subnet->net.ipv4.prefixlength = 32;
+ for(i = 0; i < 4; i++)
+ subnet->net.ipv4.address.x[i] = x[i];
+ return subnet;
+ }
+
+ if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx",
+ &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7]) == 8)
+ {
+ subnet->type = SUBNET_IPV6;
+ subnet->net.ipv6.prefixlength = 128;
+ for(i = 0; i < 8; i++)
+ subnet->net.ipv6.address.x[i] = htons(x[i]);
+ return subnet;
+ }
+
+ if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx",
+ &x[0], &x[1], &x[2], &x[3], &x[4], &x[5]) == 6)
+ {
+ subnet->type = SUBNET_MAC;
+ for(i = 0; i < 6; i++)
+ subnet->net.mac.address.x[i] = x[i];
+ return subnet;
+ }
+
+ free(subnet);
+ return NULL;
+}
+
+char *net2str(subnet_t *subnet)
+{
+ char *netstr;
+cp
+ switch(subnet->type)
+ {
+ case SUBNET_MAC:
+ asprintf(&netstr, "%hx:%hx:%hx:%hx:%hx:%hx",
+ subnet->net.mac.address.x[0],
+ subnet->net.mac.address.x[1],
+ subnet->net.mac.address.x[2],
+ subnet->net.mac.address.x[3],
+ subnet->net.mac.address.x[4],
+ subnet->net.mac.address.x[5]);
+ break;
+ case SUBNET_IPV4:
+ asprintf(&netstr, "%hu.%hu.%hu.%hu/%d",
+ subnet->net.ipv4.address.x[0],
+ subnet->net.ipv4.address.x[1],
+ subnet->net.ipv4.address.x[2],
+ subnet->net.ipv4.address.x[3],
+ subnet->net.ipv4.prefixlength);
+ break;
+ case SUBNET_IPV6:
+ asprintf(&netstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d",
+ ntohs(subnet->net.ipv6.address.x[0]),
+ ntohs(subnet->net.ipv6.address.x[1]),
+ ntohs(subnet->net.ipv6.address.x[2]),
+ ntohs(subnet->net.ipv6.address.x[3]),
+ ntohs(subnet->net.ipv6.address.x[4]),
+ ntohs(subnet->net.ipv6.address.x[5]),
+ ntohs(subnet->net.ipv6.address.x[6]),
+ ntohs(subnet->net.ipv6.address.x[7]),
+ subnet->net.ipv6.prefixlength);
+ break;
+ default:
+ syslog(LOG_ERR, _("net2str() was called with unknown subnet type %d, exitting!"), subnet->type);
+ cp_trace();
+ exit(0);
+ }
+cp
+ return netstr;
+}
+
+/* Subnet lookup routines */
+
+subnet_t *lookup_subnet(node_t *owner, subnet_t *subnet)
+{
+cp
+ return avl_search(owner->subnet_tree, subnet);
+}
+
+subnet_t *lookup_subnet_mac(mac_t *address)
+{
+ subnet_t subnet, *p;
+cp
+ subnet.type = SUBNET_MAC;
+ memcpy(&subnet.net.mac.address, address, sizeof(mac_t));
+
+ p = (subnet_t *)avl_search(subnet_tree, &subnet);
+cp
+ return p;
+}
+
+subnet_t *lookup_subnet_ipv4(ipv4_t *address)
+{
+ subnet_t subnet, *p;
+cp
+ subnet.type = SUBNET_IPV4;
+ memcpy(&subnet.net.ipv4.address, address, sizeof(ipv4_t));
+ subnet.net.ipv4.prefixlength = 32;
+
+ do
+ {
+ /* Go find subnet */
+
+ p = (subnet_t *)avl_search_closest_smaller(subnet_tree, &subnet);
+
+ /* Check if the found subnet REALLY matches */
+cp
+ if(p)
+ {
+ if(p->type != SUBNET_IPV4)
+ {
+ p = NULL;
+ break;
+ }
+
+ if (!maskcmp((char *)address, (char *)&p->net.ipv4.address, p->net.ipv4.prefixlength, sizeof(ipv4_t)))
+ break;
+ else
+ {
+ /* Otherwise, see if there is a bigger enclosing subnet */
+
+ subnet.net.ipv4.prefixlength = p->net.ipv4.prefixlength - 1;
+ maskcpy((char *)&subnet.net.ipv4.address, (char *)&p->net.ipv4.address, subnet.net.ipv4.prefixlength, sizeof(ipv4_t));
+ }
+ }
+ } while (p);
+cp
+ return p;
+}
+
+subnet_t *lookup_subnet_ipv6(ipv6_t *address)
+{
+ subnet_t subnet, *p;
+cp
+ subnet.type = SUBNET_IPV6;
+ memcpy(&subnet.net.ipv6.address, address, sizeof(ipv6_t));
+ subnet.net.ipv6.prefixlength = 128;
+
+ do
+ {
+ /* Go find subnet */
+
+ p = (subnet_t *)avl_search_closest_smaller(subnet_tree, &subnet);
+
+ /* Check if the found subnet REALLY matches */
+
+cp
+ if(p)
+ {
+ if(p->type != SUBNET_IPV6)
+ return NULL;
+
+ if (!maskcmp((char *)address, (char *)&p->net.ipv6.address, p->net.ipv6.prefixlength, sizeof(ipv6_t)))
+ break;
+ else
+ {
+ /* Otherwise, see if there is a bigger enclosing subnet */
+
+ subnet.net.ipv6.prefixlength = p->net.ipv6.prefixlength - 1;
+ maskcpy((char *)&subnet.net.ipv6.address, (char *)&p->net.ipv6.address, subnet.net.ipv6.prefixlength, sizeof(ipv6_t));
+ }
+ }
+ } while (p);
+cp
+ return p;
+}
+
+void dump_subnets(void)
+{
+ char *netstr;
+ subnet_t *subnet;
+ avl_node_t *node;
+cp
+ syslog(LOG_DEBUG, _("Subnet list:"));
+ for(node = subnet_tree->head; node; node = node->next)
+ {
+ subnet = (subnet_t *)node->data;
+ netstr = net2str(subnet);
+ syslog(LOG_DEBUG, _(" %s owner %s"), netstr, subnet->owner->name);
+ free(netstr);
+ }
+ syslog(LOG_DEBUG, _("End of subnet list."));
+cp
+}
--- /dev/null
+/*
+ subnet.h -- header for subnet.c
+ Copyright (C) 2000,2001 Guus Sliepen <guus@sliepen.warande.net>,
+ 2000,2001 Ivo Timmermans <itimmermans@bigfoot.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: subnet.h,v 1.2 2002/04/09 15:26:01 zarq Exp $
+*/
+
+#ifndef __TINC_SUBNET_H__
+#define __TINC_SUBNET_H__
+
+#include "net.h"
+
+enum
+{
+ SUBNET_MAC = 0,
+ SUBNET_IPV4,
+ SUBNET_IPV6,
+ SUBNET_TYPES /* Guardian */
+};
+
+typedef struct subnet_mac_t
+{
+ mac_t address;
+ time_t lastseen;
+} subnet_mac_t;
+
+typedef struct subnet_ipv4_t
+{
+ ipv4_t address;
+ int prefixlength;
+} subnet_ipv4_t;
+
+typedef struct subnet_ipv6_t
+{
+ ipv6_t address;
+ int prefixlength;
+} subnet_ipv6_t;
+
+#include "node.h"
+
+typedef struct subnet_t {
+ struct node_t *owner; /* the owner of this subnet */
+ struct node_t *uplink; /* the uplink which we should send packets to for this subnet */
+
+ int type; /* subnet type (IPv4? IPv6? MAC? something even weirder?) */
+
+ /* And now for the actual subnet: */
+
+ union net
+ {
+ subnet_mac_t mac;
+ subnet_ipv4_t ipv4;
+ subnet_ipv6_t ipv6;
+ } net;
+} subnet_t;
+
+extern subnet_t *new_subnet(void);
+extern void free_subnet(subnet_t *);
+extern void init_subnets(void);
+extern void exit_subnets(void);
+extern avl_tree_t *new_subnet_tree(void);
+extern void free_subnet_tree(avl_tree_t *);
+extern void subnet_add(struct node_t *, subnet_t *);
+extern void subnet_del(struct node_t *, subnet_t *);
+extern char *net2str(subnet_t *);
+extern subnet_t *str2net(char *);
+extern subnet_t *lookup_subnet(struct node_t *, subnet_t *);
+extern subnet_t *lookup_subnet_mac(mac_t *);
+extern subnet_t *lookup_subnet_ipv4(ipv4_t *);
+extern subnet_t *lookup_subnet_ipv6(ipv6_t *);
+extern void dump_subnets(void);
+
+#endif /* __TINC_SUBNET_H__ */
/*
tincd.c -- the main file for tincd
- Copyright (C) 1998,1999,2000 Ivo Timmermans <itimmermans@bigfoot.com>
- 2000 Guus Sliepen <guus@sliepen.warande.net>
+ Copyright (C) 1998-2002 Ivo Timmermans <itimmermans@bigfoot.com>
+ 2000-2002 Guus Sliepen <guus@sliepen.warande.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- $Id: tincd.c,v 1.11 2000/10/18 20:12:10 zarq Exp $
+ $Id: tincd.c,v 1.12 2002/04/09 15:26:01 zarq Exp $
*/
#include "config.h"
#include <errno.h>
-#include <fcntl.h>
+#include <fcntl.h>
#include <getopt.h>
#include <signal.h>
#include <stdio.h>
#include <syslog.h>
#include <unistd.h>
#include <signal.h>
+#include <string.h>
+#include <termios.h>
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
-#include <pidfile.h>
+#include <openssl/rand.h>
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+#include <openssl/evp.h>
+
#include <utils.h>
#include <xalloc.h>
#include "conf.h"
-#include "encr.h"
#include "net.h"
#include "netutl.h"
+#include "process.h"
#include "protocol.h"
+#include "subnet.h"
#include "system.h"
char *program_name;
/* If nonzero, display usage information and exit. */
-static int show_help;
+int show_help;
/* If nonzero, print the version on standard output and exit. */
-static int show_version;
+int show_version;
/* If nonzero, it will attempt to kill a running tincd and exit. */
-static int kill_tincd = 0;
+int kill_tincd = 0;
+
+/* If nonzero, generate public/private keypair for this host/net. */
+int generate_keys = 0;
-/* If zero, don't detach from the terminal. */
-static int do_detach = 1;
+/* If nonzero, use null ciphers and skip all key exchanges. */
+int bypass_security = 0;
char *identname; /* program name for syslog */
char *pidfilename; /* pid file location */
-static pid_t ppid; /* pid of non-detached part */
char **g_argv; /* a copy of the cmdline arguments */
-
-void cleanup_and_exit(int);
-int detach(void);
-int kill_other(void);
-void make_names(void);
-RETSIGTYPE parent_exit(int a);
-void setup_signals(void);
-int write_pidfile(void);
+char **environment; /* A pointer to the environment on
+ startup */
static struct option const long_options[] =
{
- { "kill", no_argument, NULL, 'k' },
+ { "config", required_argument, NULL, 'c' },
+ { "kill", optional_argument, NULL, 'k' },
{ "net", required_argument, NULL, 'n' },
- { "timeout", required_argument, NULL, 'p' },
{ "help", no_argument, &show_help, 1 },
{ "version", no_argument, &show_version, 1 },
{ "no-detach", no_argument, &do_detach, 0 },
+ { "generate-keys", optional_argument, NULL, 'K'},
+ { "debug", optional_argument, NULL, 'd'},
+ { "bypass-security", no_argument, &bypass_security, 1 },
{ NULL, 0, NULL, 0 }
};
else
{
printf(_("Usage: %s [option]...\n\n"), program_name);
- printf(_(" -c, --config=DIR Read configuration options from DIR.\n"
- " -D, --no-detach Don't fork and detach.\n"
- " -d Increase debug level.\n"
- " -k, --kill Attempt to kill a running tincd and exit.\n"
- " -n, --net=NETNAME Connect to net NETNAME.\n"
- " -t, --timeout=TIMEOUT Seconds to wait before giving a timeout.\n"));
- printf(_(" --help Display this help and exit.\n"
- " --version Output version information and exit.\n\n"));
+ printf(_(" -c, --config=DIR Read configuration options from DIR.\n"
+ " -D, --no-detach Don't fork and detach.\n"
+ " -d, --debug[=LEVEL] Increase debug level or set it to LEVEL.\n"
+ " -k, --kill[=SIGNAL] Attempt to kill a running tincd and exit.\n"
+ " -n, --net=NETNAME Connect to net NETNAME.\n"));
+ printf(_(" -K, --generate-keys[=BITS] Generate public/private RSA keypair.\n"
+ " --help Display this help and exit.\n"
+ " --version Output version information and exit.\n\n"));
printf(_("Report bugs to tinc@nl.linux.org.\n"));
}
exit(status);
{
int r;
int option_index = 0;
- config_t *p;
- while((r = getopt_long(argc, argv, "c:Ddkn:t:", long_options, &option_index)) != EOF)
+ while((r = getopt_long(argc, argv, "c:Dd::k::n:K::", long_options, &option_index)) != EOF)
{
switch(r)
{
case 0: /* long option */
break;
- case 'c': /* config file */
- confbase = xmalloc(strlen(optarg)+1);
- strcpy(confbase, optarg);
- break;
- case 'D': /* no detach */
- do_detach = 0;
- break;
- case 'd': /* inc debug level */
- debug_lvl++;
- break;
- case 'k': /* kill old tincds */
- kill_tincd = 1;
- break;
- case 'n': /* net name given */
- netname = xmalloc(strlen(optarg)+1);
- strcpy(netname, optarg);
- break;
- case 't': /* timeout */
- if(!(p = add_config_val(&config, TYPE_INT, optarg)))
- {
- printf(_("Invalid timeout value `%s'.\n"), optarg);
- usage(1);
- }
- break;
+ case 'c': /* config file */
+ confbase = xmalloc(strlen(optarg)+1);
+ strcpy(confbase, optarg);
+ break;
+ case 'D': /* no detach */
+ do_detach = 0;
+ break;
+ case 'd': /* inc debug level */
+ if(optarg)
+ debug_lvl = atoi(optarg);
+ else
+ debug_lvl++;
+ break;
+ case 'k': /* kill old tincds */
+ if(optarg)
+ {
+ if(!strcasecmp(optarg, "HUP"))
+ kill_tincd = SIGHUP;
+ else if(!strcasecmp(optarg, "TERM"))
+ kill_tincd = SIGTERM;
+ else if(!strcasecmp(optarg, "KILL"))
+ kill_tincd = SIGKILL;
+ else if(!strcasecmp(optarg, "USR1"))
+ kill_tincd = SIGUSR1;
+ else if(!strcasecmp(optarg, "USR2"))
+ kill_tincd = SIGUSR2;
+ else if(!strcasecmp(optarg, "WINCH"))
+ kill_tincd = SIGWINCH;
+ else if(!strcasecmp(optarg, "INT"))
+ kill_tincd = SIGINT;
+ else if(!strcasecmp(optarg, "ALRM"))
+ kill_tincd = SIGALRM;
+ else
+ {
+ kill_tincd = atoi(optarg);
+ if(!kill_tincd)
+ {
+ fprintf(stderr, _("Invalid argument `%s'; SIGNAL must be a number or one of HUP, TERM, KILL, USR1, USR2, WINCH, INT or ALRM.\n"), optarg);
+ usage(1);
+ }
+ }
+ }
+ else
+ kill_tincd = SIGTERM;
+ break;
+ case 'n': /* net name given */
+ netname = xmalloc(strlen(optarg)+1);
+ strcpy(netname, optarg);
+ break;
+ case 'K': /* generate public/private keypair */
+ if(optarg)
+ {
+ generate_keys = atoi(optarg);
+ if(generate_keys < 512)
+ {
+ fprintf(stderr, _("Invalid argument `%s'; BITS must be a number equal to or greater than 512.\n"),
+ optarg);
+ usage(1);
+ }
+ generate_keys &= ~7; /* Round it to bytes */
+ }
+ else
+ generate_keys = 1024;
+ break;
case '?':
usage(1);
default:
}
}
-void memory_full(int size)
+/* This function prettyprints the key generation process */
+
+void indicator(int a, int b, void *p)
{
- syslog(LOG_ERR, _("Memory exhausted (last is %s:%d) (couldn't allocate %d bytes), exiting."), cp_file, cp_line, size);
- exit(1);
+ switch(a)
+ {
+ case 0:
+ fprintf(stderr, ".");
+ break;
+ case 1:
+ fprintf(stderr, "+");
+ break;
+ case 2:
+ fprintf(stderr, "-");
+ break;
+ case 3:
+ switch(b)
+ {
+ case 0:
+ fprintf(stderr, " p\n");
+ break;
+ case 1:
+ fprintf(stderr, " q\n");
+ break;
+ default:
+ fprintf(stderr, "?");
+ }
+ break;
+ default:
+ fprintf(stderr, "?");
+ }
}
/*
- Detach from current terminal, write pidfile, kill parent
+ Generate a public/private RSA keypair, and ask for a file to store
+ them in.
*/
-int detach(void)
+int keygen(int bits)
{
- int fd;
- pid_t pid;
+ RSA *rsa_key;
+ FILE *f;
+ char *name = NULL;
+ char *filename;
- if(do_detach)
- {
- ppid = getpid();
-
- if((pid = fork()) < 0)
- {
- perror("fork");
- return -1;
- }
- if(pid) /* parent process */
- {
- signal(SIGTERM, parent_exit);
-// sleep(600); /* wait 10 minutes */
- exit(1);
- }
- }
-
- if(write_pidfile())
- return -1;
+ fprintf(stderr, _("Generating %d bits keys:\n"), bits);
+ rsa_key = RSA_generate_key(bits, 0xFFFF, indicator, NULL);
- if(do_detach)
+ if(!rsa_key)
{
- if((fd = open("/dev/tty", O_RDWR)) >= 0)
- {
- if(ioctl(fd, TIOCNOTTY, NULL))
- {
- perror("ioctl");
- return -1;
- }
- close(fd);
- }
-
- if(setsid() < 0)
- return -1;
-
- kill(ppid, SIGTERM);
+ fprintf(stderr, _("Error during key generation!\n"));
+ return -1;
}
-
- chdir("/"); /* avoid keeping a mointpoint busy */
-
- openlog(identname, LOG_CONS | LOG_PID, LOG_DAEMON);
-
- if(debug_lvl > 0)
- syslog(LOG_NOTICE, _("tincd %s (%s %s) starting, debug level %d"),
- VERSION, __DATE__, __TIME__, debug_lvl);
else
- syslog(LOG_NOTICE, _("tincd %s starting"), VERSION, debug_lvl);
-
- xalloc_fail_func = memory_full;
-
- return 0;
-}
-
-/*
- Close network connections, and terminate neatly
-*/
-void cleanup_and_exit(int c)
-{
- close_network_connections();
-
- if(debug_lvl > 0)
- syslog(LOG_INFO, _("Total bytes written: tap %d, socket %d; bytes read: tap %d, socket %d"),
- total_tap_out, total_socket_out, total_tap_in, total_socket_in);
+ fprintf(stderr, _("Done.\n"));
- closelog();
- kill(ppid, SIGTERM);
- exit(c);
-}
+ get_config_string(lookup_config(config_tree, "Name"), &name);
-/*
- check for an existing tinc for this net, and write pid to pidfile
-*/
-int write_pidfile(void)
-{
- int pid;
+ if(name)
+ asprintf(&filename, "%s/hosts/%s", confbase, name);
+ else
+ asprintf(&filename, "%s/rsa_key.pub", confbase);
- if((pid = check_pid(pidfilename)))
- {
- if(netname)
- fprintf(stderr, _("A tincd is already running for net `%s' with pid %d.\n"),
- netname, pid);
- else
- fprintf(stderr, _("A tincd is already running with pid %d.\n"), pid);
- return 1;
- }
+ if((f = ask_and_safe_open(filename, _("public RSA key"), "a")) == NULL)
+ return -1;
- /* if it's locked, write-protected, or whatever */
- if(!write_pid(pidfilename))
- return 1;
+ if(ftell(f))
+ fprintf(stderr, _("Appending key to existing contents.\nMake sure only one key is stored in the file.\n"));
- return 0;
-}
+ PEM_write_RSAPublicKey(f, rsa_key);
+ fclose(f);
+ free(filename);
-/*
- kill older tincd for this net
-*/
-int kill_other(void)
-{
- int pid;
+ asprintf(&filename, "%s/rsa_key.priv", confbase);
+ if((f = ask_and_safe_open(filename, _("private RSA key"), "a")) == NULL)
+ return -1;
- if(!(pid = read_pid(pidfilename)))
- {
- if(netname)
- fprintf(stderr, _("No other tincd is running for net `%s'.\n"), netname);
- else
- fprintf(stderr, _("No other tincd is running.\n"));
- return 1;
- }
+ if(ftell(f))
+ fprintf(stderr, _("Appending key to existing contents.\nMake sure only one key is stored in the file.\n"));
- errno = 0; /* No error, sometimes errno is only changed on error */
- /* ESRCH is returned when no process with that pid is found */
- if(kill(pid, SIGTERM) && errno == ESRCH)
- fprintf(stderr, _("Removing stale lock file.\n"));
- remove_pid(pidfilename);
+ PEM_write_RSAPrivateKey(f, rsa_key, NULL, NULL, 0, NULL, NULL);
+ fclose(f);
+ free(filename);
return 0;
}
if(netname)
{
if(!pidfilename)
- asprintf(&pidfilename, "/var/run/tinc.%s.pid", netname);
+ asprintf(&pidfilename, LOCALSTATEDIR "/run/tinc.%s.pid", netname);
if(!confbase)
asprintf(&confbase, "%s/tinc/%s", CONFDIR, netname);
+ else
+ syslog(LOG_INFO, _("Both netname and configuration directory given, using the latter..."));
if(!identname)
asprintf(&identname, "tinc.%s", netname);
}
else
{
- netname = "bla";
if(!pidfilename)
- pidfilename = "/var/run/tinc.pid";
+ pidfilename = LOCALSTATEDIR "/run/tinc.pid";
if(!confbase)
asprintf(&confbase, "%s/tinc", CONFDIR);
if(!identname)
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
- /* Do some intl stuff right now */
-
- unknown = _("unknown");
-
+ environment = envp;
parse_options(argc, argv, envp);
if(show_version)
{
printf(_("%s version %s (built %s %s, protocol %d)\n"), PACKAGE, VERSION, __DATE__, __TIME__, PROT_CURRENT);
- printf(_("Copyright (C) 1998,1999,2000 Ivo Timmermans, Guus Sliepen and others.\n"
- "See the AUTHORS file for a complete list.\n\n"
- "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
- "and you are welcome to redistribute it under certain conditions;\n"
- "see the file COPYING for details.\n"));
+ printf(_("Copyright (C) 1998-2002 Ivo Timmermans, Guus Sliepen and others.\n"
+ "See the AUTHORS file for a complete list.\n\n"
+ "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
+ "and you are welcome to redistribute it under certain conditions;\n"
+ "see the file COPYING for details.\n"));
return 0;
}
if(show_help)
usage(0);
- if(geteuid())
- {
- fprintf(stderr, _("You must be root to run this program. Sorry.\n"));
- return 1;
- }
+#ifdef HAVE_SOLARIS
+ openlog("tinc", LOG_CONS, LOG_DAEMON); /* Catch all syslog() calls issued before detaching */
+#else
+ openlog("tinc", LOG_PERROR, LOG_DAEMON); /* Catch all syslog() calls issued before detaching */
+#endif
g_argv = argv;
make_names();
+ init_configuration(&config_tree);
- if(kill_tincd)
- exit(kill_other());
+ /* Slllluuuuuuurrrrp! */
+cp
+ RAND_load_file("/dev/urandom", 1024);
- if(read_server_config())
- return 1;
+#ifdef HAVE_SSLEAY_ADD_ALL_ALGORITHMS
+ SSLeay_add_all_algorithms();
+#else
+ OpenSSL_add_all_algorithms();
+#endif
- setup_signals();
+cp
+ if(generate_keys)
+ {
+ read_server_config();
+ exit(keygen(generate_keys));
+ }
+
+ if(kill_tincd)
+ exit(kill_other(kill_tincd));
+ if(read_server_config())
+ exit(1);
+cp
if(detach())
exit(0);
-
-/* FIXME: wt* is this suppose to do?
- if(security_init())
- return 1;
-*/
+cp
for(;;)
{
if(!setup_network_connections())
{
main_loop();
cleanup_and_exit(1);
- }
-
+ }
+
syslog(LOG_ERR, _("Unrecoverable error"));
cp_trace();
if(do_detach)
{
- syslog(LOG_NOTICE, _("Restarting in %d seconds!"), MAXTIMEOUT);
- sleep(MAXTIMEOUT);
+ syslog(LOG_NOTICE, _("Restarting in %d seconds!"), maxtimeout);
+ sleep(maxtimeout);
}
else
{
- syslog(LOG_ERR, _("Aieee! Not restarting."));
- exit(0);
+ syslog(LOG_ERR, _("Not restarting."));
+ exit(1);
}
}
}
-
-RETSIGTYPE
-sigterm_handler(int a)
-{
- if(debug_lvl > 0)
- syslog(LOG_NOTICE, _("Got TERM signal"));
- cleanup_and_exit(0);
-}
-
-RETSIGTYPE
-sigquit_handler(int a)
-{
- if(debug_lvl > 0)
- syslog(LOG_NOTICE, _("Got QUIT signal"));
- cleanup_and_exit(0);
-}
-
-RETSIGTYPE
-sigsegv_square(int a)
-{
- syslog(LOG_ERR, _("Got another SEGV signal: not restarting"));
- exit(0);
-}
-
-RETSIGTYPE
-sigsegv_handler(int a)
-{
- syslog(LOG_ERR, _("Got SEGV signal"));
- cp_trace();
-
- if(do_detach)
- {
- syslog(LOG_NOTICE, _("Trying to re-execute in 5 seconds..."));
- signal(SIGSEGV, sigsegv_square);
- close_network_connections();
- sleep(5);
- remove_pid(pidfilename);
- execvp(g_argv[0], g_argv);
- }
- else
- {
- syslog(LOG_NOTICE, _("Aieee! Not restarting."));
- exit(0);
- }
-}
-
-RETSIGTYPE
-sighup_handler(int a)
-{
- if(debug_lvl > 0)
- syslog(LOG_NOTICE, _("Got HUP signal, rereading configuration and restarting"));
- sighup = 1;
-}
-
-RETSIGTYPE
-sigint_handler(int a)
-{
- if(debug_lvl > 0)
- syslog(LOG_NOTICE, _("Got INT signal, exiting"));
- cleanup_and_exit(0);
-}
-
-RETSIGTYPE
-sigusr1_handler(int a)
-{
- dump_conn_list();
-}
-
-RETSIGTYPE
-sigusr2_handler(int a)
-{
- if(debug_lvl > 1)
- syslog(LOG_NOTICE, _("Got USR2 signal, forcing new key generation"));
-/* FIXME: reprogram this.
- regenerate_keys();
-*/
-}
-
-RETSIGTYPE
-sighuh(int a)
-{
- syslog(LOG_WARNING, _("Got unexpected signal %d (%s)"), a, strsignal(a));
- cp_trace();
-}
-
-void
-setup_signals(void)
-{
- int i;
-
- for(i=0;i<32;i++)
- signal(i,sighuh);
-
- if(signal(SIGTERM, SIG_IGN) != SIG_ERR)
- signal(SIGTERM, sigterm_handler);
- if(signal(SIGQUIT, SIG_IGN) != SIG_ERR)
- signal(SIGQUIT, sigquit_handler);
- if(signal(SIGSEGV, SIG_IGN) != SIG_ERR)
- signal(SIGSEGV, sigsegv_handler);
- if(signal(SIGHUP, SIG_IGN) != SIG_ERR)
- signal(SIGHUP, sighup_handler);
- signal(SIGPIPE, SIG_IGN);
- if(signal(SIGINT, SIG_IGN) != SIG_ERR)
- signal(SIGINT, sigint_handler);
- signal(SIGUSR1, sigusr1_handler);
- signal(SIGUSR2, sigusr2_handler);
-// signal(SIGCHLD, parent_exit);
-}
-
-RETSIGTYPE parent_exit(int a)
-{
- exit(0);
-}
#endif
#define N_(Text) Text
+#ifndef HAVE_STRSIGNAL
+# define strsignal(p) ""
+#endif
+
+/* Other functions */
+#include <dropin.h>
+
#endif /* __TINC_SYSTEM_H__ */