c27f254dd4743a75430b01e438277cb9e3ee33cc
[tinc] / src / protocol_node.c
1 /*
2     protocol_node.c -- handle the meta-protocol, nodes
3     Copyright (C) 1999-2002 Ivo Timmermans <ivo@o2w.nl>,
4                   2000-2002 Guus Sliepen <guus@sliepen.eu.org>
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20     $Id: protocol_node.c,v 1.1.4.5 2002/09/04 08:33:08 guus Exp $
21 */
22
23 #include "config.h"
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include <syslog.h>
28 #include <stdio.h>
29 #include <stdarg.h>
30 #include <errno.h>
31
32 #include <utils.h>
33 #include <xalloc.h>
34 #include <avl_tree.h>
35
36 #include "conf.h"
37 #include "net.h"
38 #include "netutl.h"
39 #include "protocol.h"
40 #include "meta.h"
41 #include "connection.h"
42 #include "node.h"
43
44 #include "system.h"
45
46 int send_add_node(connection_t *c, node_t *n)
47 {
48   int x;
49   char *address, *port;
50 cp
51   if(!n->status.reachable)
52     return 0;
53
54   sockaddr2str(&n->address, &address, &port);
55   x = send_request(c, "%d %s %s %s %lx %d %s %s", ADD_NODE,
56                       n->name, address, port,
57                       n->options, n->distance + 1, // Alternatively, use n->distance + c->estimated_weight
58                       n->prevhop->name, n->via->name);
59   free(address);
60   free(port);
61 cp
62   return x;
63 }
64
65 int add_node_h(connection_t *c)
66 {
67   connection_t *other;
68   node_t *n, *prevhop, *via;
69   char name[MAX_STRING_SIZE];
70   char address[MAX_STRING_SIZE];
71   char port[MAX_STRING_SIZE];
72   char prevhopname[MAX_STRING_SIZE];
73   char vianame[MAX_STRING_SIZE];
74   long int options;
75   int distance;
76   avl_node_t *node;
77 cp
78   if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %lx %d "MAX_STRING" "MAX_STRING,
79             name, address, port, &options, &distance, prevhopname, vianame) != 7)
80     {
81        syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "ADD_NODE", c->name, c->hostname);
82        return -1;
83     }
84
85   /* Check if names are valid */
86
87   if(check_id(name))
88     {
89       syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_NODE", c->name, c->hostname, _("invalid name"));
90       return -1;
91     }
92
93   /* This node is indirect if it's nexthop is as well */
94   
95   if(c->node->options & OPTION_INDIRECT)
96     options |= OPTION_INDIRECT;
97
98   /* Lookup nodes */
99
100   prevhop = lookup_node(prevhopname);
101   
102   if(!prevhop)
103     {
104       prevhop = new_node();
105       prevhop->name = xstrdup(prevhopname);
106       node_add(prevhop);
107     }
108
109   via = lookup_node(vianame);
110   
111   if(!via)
112     {
113       via = new_node();
114       via->name = xstrdup(vianame);
115       node_add(via);
116     }
117
118   n = lookup_node(name);
119   
120   if(!n)
121     {
122       // It's a new node. Add it and tell the others.
123       n = new_node();
124       n->name = xstrdup(name);
125       n->address = str2sockaddr(address, port);
126       n->hostname = sockaddr2hostname(&n->address);
127       n->options = options;
128       n->distance = distance;
129       n->nexthop = c->node;
130       n->prevhop = prevhop;
131       n->via = via;
132       node_add(n);
133       if(prevhop == myself)
134         {
135           syslog(LOG_WARNING, _("Got ADD_NODE %s prevhop %s via %s from %s, sending back a DEL_NODE!"), name, prevhopname, vianame, c->name);
136           send_del_node(c, n);
137           return 0;
138         }
139       n->status.reachable = 1;
140     }
141   else
142     {
143       // If this ADD_NODE is closer or more direct, use it instead of the old one.
144       if(!n->status.reachable || ((n->options & OPTION_INDIRECT) && !(options & OPTION_INDIRECT)) || n->distance > distance)
145         {
146           if(prevhop == myself)
147             {
148               syslog(LOG_WARNING, _("Got ADD_NODE %s prevhop %s via %s from %s!"), name, prevhopname, vianame, c->name);
149               send_del_node(c, n);
150               return 0;
151             }
152           node = avl_unlink(node_udp_tree, n);
153           n->address = str2sockaddr(address, port);
154           avl_insert_node(node_udp_tree, node);
155           if(n->hostname)
156             free(n->hostname);
157           n->hostname = sockaddr2hostname(&n->address);
158           n->options = options;
159           n->distance = distance;
160           n->via = n->nexthop = c->node;
161           n->status.reachable = 1;
162           n->status.validkey = 0;
163           n->status.waitingforkey = 0;
164         }
165       else
166         // Otherwise, just ignore it.
167         return 0;
168     }
169
170   /* Tell the rest about the new node */
171
172   for(node = connection_tree->head; node; node = node->next)
173     {
174       other = (connection_t *)node->data;
175       if(other->status.active && other != c)
176         send_add_node(other, n);
177     }
178
179 cp
180   return 0;
181 }
182
183 int send_del_node(connection_t *c, node_t *n)
184 {
185 cp
186   return send_request(c, "%d %s %s", DEL_NODE, n->name, n->prevhop->name);
187 }
188
189 int del_node_h(connection_t *c)
190 {
191   char name[MAX_STRING_SIZE];
192   char prevhopname[MAX_STRING_SIZE];
193   node_t *n, *prevhop;
194   connection_t *other;
195   avl_node_t *node;
196 cp
197   if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING, name, prevhopname) != 2)
198     {
199       syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "DEL_NODE",
200              c->name, c->hostname);
201       return -1;
202     }
203
204   /* Check if names are valid */
205
206   if(check_id(name))
207     {
208       syslog(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_NODE", c->name, c->hostname, _("invalid name"));
209       return -1;
210     }
211
212   /* Lookup nodes */
213
214   n = lookup_node(name);
215   prevhop = lookup_node(prevhopname);
216
217   if(!n || !prevhop)
218     {
219       if(debug_lvl >= DEBUG_PROTOCOL)
220         syslog(LOG_WARNING, _("Got %s from %s (%s) which does not appear in the node tree"), "DEL_NODE", c->name, c->hostname);
221       return 0;
222     }
223
224   /* If we got a DEL_NODE but we know of a different route to it, tell the one who send the DEL_NODE */
225
226   if(n->nexthop != c->node || n->prevhop != prevhop)
227     {
228       return send_add_node(c, n);
229     }
230   
231   /* Otherwise, tell the rest about the deleted node */
232
233   for(node = connection_tree->head; node; node = node->next)
234     {
235       other = (connection_t *)node->data;
236       if(other->status.active && other != c)
237         send_del_node(other, n);
238     }
239
240   /* "Delete" the node */
241   
242   n->status.reachable = 0;
243   n->status.validkey = 0;
244 cp
245   return 0;
246 }