From: Ivo Timmermans Date: Tue, 9 Apr 2002 15:26:01 +0000 (+0000) Subject: Updating HEAD branch #4; Merging CABAL -> HEAD. X-Git-Tag: release-1.0.3~84 X-Git-Url: https://tinc-vpn.org/git/browse?a=commitdiff_plain;h=462ab530e546f5732dfd51134751da6f6910d679;p=tinc Updating HEAD branch #4; Merging CABAL -> HEAD. --- diff --git a/acconfig.h b/acconfig.h index f1d9ee74..41fdb995 100644 --- a/acconfig.h +++ b/acconfig.h @@ -36,7 +36,6 @@ /* Define to 1 if you have the stpcpy function. */ #undef HAVE_STPCPY - /* For getopt */ #if HAVE_STDLIB_H # define getopt system_getopt @@ -44,6 +43,20 @@ # 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 @@ -53,3 +66,12 @@ /* 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 diff --git a/doc/es/Makefile.am b/doc/es/Makefile.am new file mode 100644 index 00000000..756d6707 --- /dev/null +++ b/doc/es/Makefile.am @@ -0,0 +1,3 @@ +## Process this file with automake to get Makefile.in + +# Nothing to see here, go away! diff --git a/doc/sample-config/hosts/alpha b/doc/sample-config/hosts/alpha new file mode 100644 index 00000000..0f5e56a9 --- /dev/null +++ b/doc/sample-config/hosts/alpha @@ -0,0 +1,15 @@ +# 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----- diff --git a/doc/sample-config/hosts/beta b/doc/sample-config/hosts/beta new file mode 100644 index 00000000..6f70d4f7 --- /dev/null +++ b/doc/sample-config/hosts/beta @@ -0,0 +1,16 @@ +# 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----- diff --git a/doc/sample-config/rsa_key.priv b/doc/sample-config/rsa_key.priv new file mode 100644 index 00000000..ac135363 --- /dev/null +++ b/doc/sample-config/rsa_key.priv @@ -0,0 +1 @@ +# Generate this file with `tincd -n example -K` diff --git a/doc/sample-config/tinc-down b/doc/sample-config/tinc-down new file mode 100644 index 00000000..65b049e2 --- /dev/null +++ b/doc/sample-config/tinc-down @@ -0,0 +1,4 @@ +#!/bin/sh +# This file closes down the tap device. + +ifconfig $INTERFACE down diff --git a/doc/sample-config/tinc-up b/doc/sample-config/tinc-up new file mode 100644 index 00000000..8f05c4a5 --- /dev/null +++ b/doc/sample-config/tinc-up @@ -0,0 +1,15 @@ +#!/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 diff --git a/doc/sample-config/tinc.conf b/doc/sample-config/tinc.conf new file mode 100644 index 00000000..f5f0aa63 --- /dev/null +++ b/doc/sample-config/tinc.conf @@ -0,0 +1,25 @@ +# 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 diff --git a/lib/avl_tree.c b/lib/avl_tree.c new file mode 100644 index 00000000..6ad4c991 --- /dev/null +++ b/lib/avl_tree.c @@ -0,0 +1,723 @@ +/* + avl_tree.c -- avl_ tree and linked list convenience + Copyright (C) 1998 Michael H. Buselli + 2000,2001 Ivo Timmermans , + 2000,2001 Guus Sliepen + 2000,2001 Wessel Dankers + + 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 . + + Modified 2000-11-28 by Wessel Dankers 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 . + + $Id: avl_tree.c,v 1.2 2002/04/09 15:26:00 zarq Exp $ +*/ + +#include +#include +#include + +#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 diff --git a/lib/avl_tree.h b/lib/avl_tree.h new file mode 100644 index 00000000..9022097f --- /dev/null +++ b/lib/avl_tree.h @@ -0,0 +1,145 @@ +/* + avl_tree.h -- header file for avl_tree.c + Copyright (C) 1998 Michael H. Buselli + 2000,2001 Ivo Timmermans , + 2000,2001 Guus Sliepen + 2000,2001 Wessel Dankers + + 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 . + + Modified 2000-11-28 by Wessel Dankers 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 . + + $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__ */ diff --git a/lib/dropin.c b/lib/dropin.c new file mode 100644 index 00000000..15ee90c1 --- /dev/null +++ b/lib/dropin.c @@ -0,0 +1,171 @@ +/* + dropin.c -- a set of drop-in replacements for libc functions + Copyright (C) 2000,2001 Ivo Timmermans , + 2000,2001 Guus Sliepen + + 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 +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#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 diff --git a/lib/dropin.h b/lib/dropin.h new file mode 100644 index 00000000..1f4157b9 --- /dev/null +++ b/lib/dropin.h @@ -0,0 +1,38 @@ +/* + dropin.h -- header file for dropin.c + Copyright (C) 2000,2001 Ivo Timmermans , + 2000,2001 Guus Sliepen + + 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__ */ diff --git a/lib/list.c b/lib/list.c index 5358f198..ae238514 100644 --- a/lib/list.c +++ b/lib/list.c @@ -1,7 +1,7 @@ /* list.c -- functions to deal with double linked lists - Copyright (C) 2000 Ivo Timmermans - 2000 Guus Sliepen + Copyright (C) 2000,2001 Ivo Timmermans + 2000,2001 Guus Sliepen 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 @@ -17,130 +17,181 @@ 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 +#include -#include -#include #include - #include -/* - 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); + } } diff --git a/lib/list.h b/lib/list.h index 91628332..3f2a901c 100644 --- a/lib/list.h +++ b/lib/list.h @@ -1,7 +1,7 @@ /* list.h -- header file for list.c - Copyright (C) 2000 Ivo Timmermans - 2000 Guus Sliepen + Copyright (C) 2000,2001 Ivo Timmermans + 2000,2001 Guus Sliepen 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 @@ -17,28 +17,66 @@ 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__ */ diff --git a/lib/pidfile.c b/lib/pidfile.c index 2f30a4e8..a954d18f 100644 --- a/lib/pidfile.c +++ b/lib/pidfile.c @@ -32,6 +32,8 @@ #include #include #include +#include +#include /* read_pid * @@ -71,6 +73,7 @@ int check_pid (char *pidfile) * be found -- GW */ /* But... errno is usually changed only on error.. */ + errno = 0; if (kill(pid, 0) && errno == ESRCH) return(0); @@ -93,13 +96,15 @@ int write_pid (char *pidfile) 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)) { @@ -109,11 +114,13 @@ int write_pid (char *pidfile) } 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; diff --git a/lib/rbl.c b/lib/rbl.c new file mode 100644 index 00000000..d2a2dc7f --- /dev/null +++ b/lib/rbl.c @@ -0,0 +1,596 @@ +/* + rbl.c -- red-black tree + linked list convenience + Copyright (C) 2000 Ivo Timmermans , + 2000 Guus Sliepen + + 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 +#include + +#include "rbl.h" +#include + +/* 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); + } +} diff --git a/lib/rbl.h b/lib/rbl.h new file mode 100644 index 00000000..906ae313 --- /dev/null +++ b/lib/rbl.h @@ -0,0 +1,104 @@ +/* + rbl.h -- header file for rbl.c + Copyright (C) 2000 Ivo Timmermans , + 2000 Guus Sliepen + + 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__ */ diff --git a/lib/utils.c b/lib/utils.c index fc7abe4c..d79532b2 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -1,7 +1,7 @@ /* utils.c -- gathering of some stupid small functions - Copyright (C) 1999,2000 Ivo Timmermans - 2000 Guus Sliepen + Copyright (C) 1999-2001 Ivo Timmermans + 2000,2001 Guus Sliepen 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 @@ -26,10 +26,13 @@ #include #include +#include -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"; @@ -59,16 +62,26 @@ void bin2hex(char *src, char *dst, int length) } } -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 diff --git a/lib/utils.h b/lib/utils.h index 46465f36..0b79bfae 100644 --- a/lib/utils.h +++ b/lib/utils.h @@ -1,7 +1,7 @@ /* utils.h -- header file for utils.c - Copyright (C) 1999,2000 Ivo Timmermans - 2000 Guus Sliepen + Copyright (C) 1999-2001 Ivo Timmermans + 2000,2001 Guus Sliepen 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 @@ -24,24 +24,34 @@ #include 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__ */ diff --git a/lib/xalloc.h b/lib/xalloc.h index 84b6cacf..caf0f37d 100644 --- a/lib/xalloc.h +++ b/lib/xalloc.h @@ -22,3 +22,5 @@ void *xmalloc PARAMS ((size_t n)); 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)); diff --git a/lib/xmalloc.c b/lib/xmalloc.c index 037fab87..e1ab3140 100644 --- a/lib/xmalloc.c +++ b/lib/xmalloc.c @@ -21,6 +21,7 @@ #include #include +#include #if STDC_HEADERS # include @@ -124,6 +125,18 @@ xrealloc (p, n) 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. */ diff --git a/src/conf.c b/src/conf.c index 442e3960..8e3d15a0 100644 --- a/src/conf.c +++ b/src/conf.c @@ -1,9 +1,9 @@ /* conf.c -- configuration code - Copyright (C) 1998 Emphyrio, - Copyright (C) 1998,1999,2000 Ivo Timmermans - 2000 Guus Sliepen - 2000 Cris van Pelt + Copyright (C) 1998 Robert van der Meulen + 1998-2002 Ivo Timmermans + 2000-2002 Guus Sliepen + 2000 Cris van Pelt 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 @@ -19,9 +19,10 @@ 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 #include @@ -30,178 +31,396 @@ #include #include #include +#include +#include +#include +#include +#include #include +#include /* for cp */ +#include #include "conf.h" -#include "netutl.h" /* for strtoip */ -#include /* 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; @@ -213,56 +432,192 @@ int read_server_config() 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; } diff --git a/src/conf.h b/src/conf.h index 13af26c0..a3cb2e70 100644 --- a/src/conf.h +++ b/src/conf.h @@ -1,7 +1,7 @@ /* conf.h -- header for conf.c - Copyright (C) 1998,1999,2000 Ivo Timmermans - 2000 Guus Sliepen + Copyright (C) 1998-2002 Ivo Timmermans + 2000-2002 Guus Sliepen 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 @@ -17,85 +17,54 @@ 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 +#include +#include -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 +#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__ */ diff --git a/src/connection.c b/src/connection.c new file mode 100644 index 00000000..4bec8a83 --- /dev/null +++ b/src/connection.c @@ -0,0 +1,137 @@ +/* + connection.c -- connection list management + Copyright (C) 2000-2002 Guus Sliepen , + 2000-2002 Ivo Timmermans + + 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 +#include +#include +#include + +#include +#include + +#include "net.h" /* Don't ask. */ +#include "netutl.h" +#include "config.h" +#include "conf.h" +#include +#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; +} diff --git a/src/connection.h b/src/connection.h new file mode 100644 index 00000000..bd37aea0 --- /dev/null +++ b/src/connection.h @@ -0,0 +1,121 @@ +/* + connection.h -- header for connection.c + Copyright (C) 2000-2002 Guus Sliepen , + 2000-2002 Ivo Timmermans + + 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 + +#include +#include + +#ifdef HAVE_OPENSSL_EVP_H +# include +#else +# include +#endif + +#ifdef HAVE_OPENSSL_RSA_H +# include +#else +# include +#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__ */ diff --git a/src/device.h b/src/device.h new file mode 100644 index 00000000..aa53395b --- /dev/null +++ b/src/device.h @@ -0,0 +1,36 @@ +/* + net.h -- generic header for device.c + Copyright (C) 2001-2002 Ivo Timmermans + 2001-2002 Guus Sliepen + + 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__ */ diff --git a/src/edge.c b/src/edge.c new file mode 100644 index 00000000..6ceec61d --- /dev/null +++ b/src/edge.c @@ -0,0 +1,211 @@ +/* + edge.c -- edge tree management + Copyright (C) 2000-2002 Guus Sliepen , + 2000-2002 Ivo Timmermans + + 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 +#include +#include + +#include +#include + +#include "net.h" /* Don't ask. */ +#include "netutl.h" +#include "config.h" +#include "conf.h" +#include +#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 +} diff --git a/src/edge.h b/src/edge.h new file mode 100644 index 00000000..f50b3349 --- /dev/null +++ b/src/edge.h @@ -0,0 +1,62 @@ +/* + edge.h -- header for edge.c + Copyright (C) 2001-2002 Guus Sliepen , + 2001-2002 Ivo Timmermans + + 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 + +#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__ */ diff --git a/src/event.c b/src/event.c new file mode 100644 index 00000000..8e332fa0 --- /dev/null +++ b/src/event.c @@ -0,0 +1,110 @@ +/* + event.c -- event queue + Copyright (C) 2002 Guus Sliepen , + 2002 Ivo Timmermans + + 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 +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/event.h b/src/event.h new file mode 100644 index 00000000..4b238ccb --- /dev/null +++ b/src/event.h @@ -0,0 +1,48 @@ +/* + event.h -- header for event.c + Copyright (C) 2002 Guus Sliepen , + 2002 Ivo Timmermans + + 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 +#include + +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__ */ diff --git a/src/graph.c b/src/graph.c new file mode 100644 index 00000000..cf5a4165 --- /dev/null +++ b/src/graph.c @@ -0,0 +1,290 @@ +/* + graph.c -- graph algorithms + Copyright (C) 2001-2002 Guus Sliepen , + 2001-2002 Ivo Timmermans + + 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 +#include +#include "config.h" +#include +#if defined(HAVE_FREEBSD) || defined(HAVE_OPENBSD) + #include +#endif +#include + +#include +#include + +#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(); +} diff --git a/src/graph.h b/src/graph.h new file mode 100644 index 00000000..ab8fff20 --- /dev/null +++ b/src/graph.h @@ -0,0 +1,25 @@ +/* + graph.h -- header for graph.c + Copyright (C) 2001-2002 Guus Sliepen , + 2001-2002 Ivo Timmermans + + 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); diff --git a/src/meta.c b/src/meta.c new file mode 100644 index 00000000..352c691f --- /dev/null +++ b/src/meta.c @@ -0,0 +1,210 @@ +/* + meta.c -- handle the meta communication + Copyright (C) 2000-2002 Guus Sliepen , + 2000-2002 Ivo Timmermans + + 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 +#include + +#include +#include +#include +#include +/* This line must be below the rest for FreeBSD */ +#include +#include + +#include + +#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; +} diff --git a/src/meta.h b/src/meta.h new file mode 100644 index 00000000..ff4d7044 --- /dev/null +++ b/src/meta.h @@ -0,0 +1,32 @@ +/* + meta.h -- header for meta.c + Copyright (C) 2000-2002 Guus Sliepen , + 2000-2002 Ivo Timmermans + + 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__ */ diff --git a/src/net.c b/src/net.c index 16ab326c..39f01fa7 100644 --- a/src/net.c +++ b/src/net.c @@ -1,7 +1,7 @@ /* net.c -- most of the network code - Copyright (C) 1998,1999,2000 Ivo Timmermans , - 2000 Guus Sliepen + Copyright (C) 1998-2002 Ivo Timmermans , + 2000-2002 Guus Sliepen 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 @@ -17,966 +17,211 @@ 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 #include #include #include #include +#ifdef HAVE_LINUX + #include + #include +#endif #include #include #include -#include -#include +#include #include #include #include #include - -#ifdef HAVE_TUNTAP +#include +/* SunOS really wants sys/socket.h BEFORE net/if.h, + and FreeBSD wants these lines below the rest. */ +#include +#include #include -#include LINUX_IF_TUN_H -#endif + +#include #include #include +#include +#include #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 } @@ -989,71 +234,42 @@ 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; } /* @@ -1062,99 +278,57 @@ cp */ 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 } @@ -1167,59 +341,108 @@ void main_loop(void) 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 diff --git a/src/net.h b/src/net.h index 05915361..0de53384 100644 --- a/src/net.h +++ b/src/net.h @@ -1,6 +1,7 @@ /* net.h -- header for net.c - Copyright (C) 1998,1999,2000 Ivo Timmermans + Copyright (C) 1998-2002 Ivo Timmermans + 2000-2002 Guus Sliepen 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 @@ -16,49 +17,47 @@ 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 +#include +#include #include #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 { @@ -69,37 +68,25 @@ typedef unsigned short port_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; @@ -111,36 +98,56 @@ typedef struct packet_queue_t { 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__ */ diff --git a/src/net_packet.c b/src/net_packet.c new file mode 100644 index 00000000..087df8a5 --- /dev/null +++ b/src/net_packet.c @@ -0,0 +1,431 @@ +/* + net_packet.c -- Handles in- and outgoing VPN packets + Copyright (C) 1998-2002 Ivo Timmermans , + 2000-2002 Guus Sliepen + + 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 +#include +#include +#include +#ifdef HAVE_LINUX + #include + #include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* SunOS really wants sys/socket.h BEFORE net/if.h, + and FreeBSD wants these lines below the rest. */ +#include +#include +#include + +#include +#include +#include +#include + +#ifndef HAVE_RAND_PSEUDO_BYTES +#define RAND_pseudo_bytes RAND_bytes +#endif + +#include + +#include +#include +#include +#include + +#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 +} + diff --git a/src/net_setup.c b/src/net_setup.c new file mode 100644 index 00000000..9591c941 --- /dev/null +++ b/src/net_setup.c @@ -0,0 +1,564 @@ +/* + net_setup.c -- Setup. + Copyright (C) 1998-2002 Ivo Timmermans , + 2000-2002 Guus Sliepen + + 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 +#include +#include +#include +#ifdef HAVE_LINUX + #include + #include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* SunOS really wants sys/socket.h BEFORE net/if.h, + and FreeBSD wants these lines below the rest. */ +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#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; +} diff --git a/src/net_socket.c b/src/net_socket.c new file mode 100644 index 00000000..cc509dc2 --- /dev/null +++ b/src/net_socket.c @@ -0,0 +1,484 @@ +/* + net_socket.c -- Handle various kinds of sockets. + Copyright (C) 1998-2002 Ivo Timmermans , + 2000-2002 Guus Sliepen + + 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 +#include +#include +#include +#ifdef HAVE_LINUX + #include + #include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* SunOS really wants sys/socket.h BEFORE net/if.h, + and FreeBSD wants these lines below the rest. */ +#include +#include +#include + +#include +#include +#include +#include + +#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); + } +} diff --git a/src/netutl.c b/src/netutl.c index f9424442..be901641 100644 --- a/src/netutl.c +++ b/src/netutl.c @@ -1,6 +1,7 @@ /* netutl.c -- some supporting network utility code - Copyright (C) 1998,1999,2000 Ivo Timmermans + Copyright (C) 1998-2002 Ivo Timmermans + 2000-2002 Guus Sliepen 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 @@ -16,119 +17,230 @@ 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 +#include #include #include #include #include #include +#include #include #include +#include #include #include #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; +} diff --git a/src/netutl.h b/src/netutl.h index 4c47a077..d30fca57 100644 --- a/src/netutl.h +++ b/src/netutl.h @@ -1,6 +1,7 @@ /* netutl.h -- header file for netutl.c - Copyright (C) 1998,1999,2000 Ivo Timmermans + Copyright (C) 1998-2002 Ivo Timmermans + 2000-2002 Guus Sliepen 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 @@ -16,16 +17,30 @@ 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 +#include +#include + #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__ */ diff --git a/src/node.c b/src/node.c new file mode 100644 index 00000000..5d417f52 --- /dev/null +++ b/src/node.c @@ -0,0 +1,173 @@ +/* + node.c -- node tree management + Copyright (C) 2001-2002 Guus Sliepen , + 2001-2002 Ivo Timmermans + + 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 +#include + +#include +#include "node.h" +#include "netutl.h" +#include "net.h" +#include +#include + +#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 +} diff --git a/src/node.h b/src/node.h new file mode 100644 index 00000000..0b615447 --- /dev/null +++ b/src/node.h @@ -0,0 +1,88 @@ +/* + node.h -- header for node.c + Copyright (C) 2001-2002 Guus Sliepen , + 2001-2002 Ivo Timmermans + + 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 + +#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__ */ diff --git a/src/process.c b/src/process.c new file mode 100644 index 00000000..8f9f01ae --- /dev/null +++ b/src/process.c @@ -0,0 +1,507 @@ +/* + process.c -- process management functions + Copyright (C) 1999-2002 Ivo Timmermans , + 2000-2002 Guus Sliepen + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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)); + } +} diff --git a/src/process.h b/src/process.h new file mode 100644 index 00000000..cd791cb5 --- /dev/null +++ b/src/process.h @@ -0,0 +1,36 @@ +/* + process.h -- header file for process.c + Copyright (C) 1999-2002 Ivo Timmermans , + 2000-2002 Guus Sliepen + + 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__ */ diff --git a/src/protocol.c b/src/protocol.c index 235aa9b1..fa43453f 100644 --- a/src/protocol.c +++ b/src/protocol.c @@ -1,7 +1,7 @@ /* - protocol.c -- handle the meta-protocol - Copyright (C) 1999,2000 Ivo Timmermans , - 2000 Guus Sliepen + protocol.c -- handle the meta-protocol, basic functions + Copyright (C) 1999-2001 Ivo Timmermans , + 2000,2001 Guus Sliepen 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 @@ -17,7 +17,7 @@ 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" @@ -27,26 +27,22 @@ #include #include #include -#include -#include #include +#include +#include #include #include -#include - -#include - #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; @@ -54,1002 +50,195 @@ int check_id(char *id) 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", }; diff --git a/src/protocol.h b/src/protocol.h index 7b14dec1..4b112440 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -1,7 +1,7 @@ /* protocol.h -- header for protocol.c - Copyright (C) 1999,2000 Ivo Timmermans , - 2000 Guus Sliepen + Copyright (C) 1999-2001 Ivo Timmermans , + 2000,2001 Guus Sliepen 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 @@ -17,63 +17,104 @@ 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__ */ diff --git a/src/protocol_auth.c b/src/protocol_auth.c new file mode 100644 index 00000000..24f8c758 --- /dev/null +++ b/src/protocol_auth.c @@ -0,0 +1,605 @@ +/* + protocol_auth.c -- handle the meta-protocol, authentication + Copyright (C) 1999-2002 Ivo Timmermans , + 2000-2002 Guus Sliepen + + 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#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; +} diff --git a/src/protocol_edge.c b/src/protocol_edge.c new file mode 100644 index 00000000..f387153c --- /dev/null +++ b/src/protocol_edge.c @@ -0,0 +1,298 @@ +/* + protocol_edge.c -- handle the meta-protocol, edges + Copyright (C) 1999-2002 Ivo Timmermans , + 2000-2002 Guus Sliepen + + 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#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; +} diff --git a/src/protocol_key.c b/src/protocol_key.c new file mode 100644 index 00000000..0fa37b9b --- /dev/null +++ b/src/protocol_key.c @@ -0,0 +1,286 @@ +/* + protocol_key.c -- handle the meta-protocol, key exchange + Copyright (C) 1999-2002 Ivo Timmermans , + 2000-2002 Guus Sliepen + + 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#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; +} diff --git a/src/protocol_misc.c b/src/protocol_misc.c new file mode 100644 index 00000000..96234e4f --- /dev/null +++ b/src/protocol_misc.c @@ -0,0 +1,198 @@ +/* + protocol_misc.c -- handle the meta-protocol, miscellaneous functions + Copyright (C) 1999-2002 Ivo Timmermans , + 2000-2002 Guus Sliepen + + 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 +#include +#include +#include +#include +#include + +#include + +#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", +}; diff --git a/src/protocol_subnet.c b/src/protocol_subnet.c new file mode 100644 index 00000000..9d93dfbf --- /dev/null +++ b/src/protocol_subnet.c @@ -0,0 +1,237 @@ +/* + protocol_subnet.c -- handle the meta-protocol, subnets + Copyright (C) 1999-2002 Ivo Timmermans , + 2000-2002 Guus Sliepen + + 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#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; +} diff --git a/src/route.c b/src/route.c new file mode 100644 index 00000000..0496721a --- /dev/null +++ b/src/route.c @@ -0,0 +1,493 @@ +/* + route.c -- routing + Copyright (C) 2000-2002 Ivo Timmermans , + 2000-2002 Guus Sliepen + + 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 +#endif +#include +#include +#if defined(HAVE_SOLARIS) || defined(HAVE_OPENBSD) + #include + #define ETHER_ADDR_LEN 6 +#else + #include +#endif +#include +#include +#include +#include +#include +#include +#include + +#include + +#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; + } +} diff --git a/src/route.h b/src/route.h new file mode 100644 index 00000000..d954abb6 --- /dev/null +++ b/src/route.h @@ -0,0 +1,41 @@ +/* + route.h -- header file for route.c + Copyright (C) 2000-2002 Ivo Timmermans + 2000-2002 Guus Sliepen + + 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__ */ diff --git a/src/subnet.c b/src/subnet.c new file mode 100644 index 00000000..5f649b51 --- /dev/null +++ b/src/subnet.c @@ -0,0 +1,394 @@ +/* + subnet.c -- handle subnet lookups and lists + Copyright (C) 2000-2002 Guus Sliepen , + 2000-2002 Ivo Timmermans + + 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 +} diff --git a/src/subnet.h b/src/subnet.h new file mode 100644 index 00000000..2369f4d7 --- /dev/null +++ b/src/subnet.h @@ -0,0 +1,88 @@ +/* + subnet.h -- header for subnet.c + Copyright (C) 2000,2001 Guus Sliepen , + 2000,2001 Ivo Timmermans + + 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__ */ diff --git a/src/tincd.c b/src/tincd.c index 87ccc7fe..c02ce5cf 100644 --- a/src/tincd.c +++ b/src/tincd.c @@ -1,7 +1,7 @@ /* tincd.c -- the main file for tincd - Copyright (C) 1998,1999,2000 Ivo Timmermans - 2000 Guus Sliepen + Copyright (C) 1998-2002 Ivo Timmermans + 2000-2002 Guus Sliepen 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 @@ -17,13 +17,13 @@ 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 -#include +#include #include #include #include @@ -31,20 +31,27 @@ #include #include #include +#include +#include #ifdef HAVE_SYS_IOCTL_H # include #endif -#include +#include +#include +#include +#include + #include #include #include "conf.h" -#include "encr.h" #include "net.h" #include "netutl.h" +#include "process.h" #include "protocol.h" +#include "subnet.h" #include "system.h" @@ -52,38 +59,37 @@ 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 } }; @@ -95,14 +101,14 @@ usage(int status) 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); @@ -113,38 +119,77 @@ parse_options(int argc, char **argv, char **envp) { 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: @@ -153,134 +198,88 @@ parse_options(int argc, char **argv, char **envp) } } -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; } @@ -293,17 +292,18 @@ void make_names(void) 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) @@ -320,20 +320,17 @@ main(int argc, char **argv, char **envp) 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; } @@ -341,164 +338,63 @@ main(int argc, char **argv, char **envp) 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); -} diff --git a/system.h b/system.h index f6b2bd8b..51adca0b 100644 --- a/system.h +++ b/system.h @@ -41,5 +41,12 @@ #endif #define N_(Text) Text +#ifndef HAVE_STRSIGNAL +# define strsignal(p) "" +#endif + +/* Other functions */ +#include + #endif /* __TINC_SYSTEM_H__ */