Commit | Line | Data |
---|---|---|
410c8acf | 1 | /* -*-c-*- |
410c8acf | 2 | * |
3 | * Communication with the peer | |
4 | * | |
5 | * (c) 2001 Straylight/Edgeware | |
6 | */ | |
7 | ||
e04c2d50 | 8 | /*----- Licensing notice --------------------------------------------------* |
410c8acf | 9 | * |
10 | * This file is part of Trivial IP Encryption (TrIPE). | |
11 | * | |
11ad66c2 MW |
12 | * TrIPE is free software: you can redistribute it and/or modify it under |
13 | * the terms of the GNU General Public License as published by the Free | |
14 | * Software Foundation; either version 3 of the License, or (at your | |
15 | * option) any later version. | |
e04c2d50 | 16 | * |
11ad66c2 MW |
17 | * TrIPE is distributed in the hope that it will be useful, but WITHOUT |
18 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
19 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
20 | * for more details. | |
e04c2d50 | 21 | * |
410c8acf | 22 | * You should have received a copy of the GNU General Public License |
11ad66c2 | 23 | * along with TrIPE. If not, see <https://www.gnu.org/licenses/>. |
410c8acf | 24 | */ |
25 | ||
410c8acf | 26 | /*----- Header files ------------------------------------------------------*/ |
27 | ||
28 | #include "tripe.h" | |
29 | ||
5d06f63e MW |
30 | /*----- Global state ------------------------------------------------------*/ |
31 | ||
32 | sel_file udpsock[NADDRFAM]; | |
33 | ||
410c8acf | 34 | /*----- Static variables --------------------------------------------------*/ |
35 | ||
c8e02c8a MW |
36 | static sym_table byname; |
37 | static addrmap byaddr; | |
6411163d | 38 | static unsigned nmobile; |
410c8acf | 39 | |
42da2a58 | 40 | /*----- Tunnel table ------------------------------------------------------*/ |
41 | ||
42 | const tunnel_ops *tunnels[] = { | |
43 | #ifdef TUN_LINUX | |
44 | &tun_linux, | |
45 | #endif | |
46 | #ifdef TUN_BSD | |
47 | &tun_bsd, | |
48 | #endif | |
49 | #ifdef TUN_UNET | |
50 | &tun_unet, | |
51 | #endif | |
52 | &tun_slip, | |
53 | 0 | |
54 | }, *tun_default; | |
55 | ||
410c8acf | 56 | /*----- Main code ---------------------------------------------------------*/ |
57 | ||
0ba8de86 | 58 | /* --- @p_pingtype@ --- * |
59 | * | |
60 | * Arguments: @unsigned msg@ = message type | |
61 | * | |
62 | * Returns: String to describe the message. | |
63 | */ | |
64 | ||
65 | static const char *p_pingtype(unsigned msg) | |
66 | { | |
67 | switch (msg & MSG_TYPEMASK) { | |
68 | case MISC_PING: | |
69 | case MISC_PONG: | |
70 | return "transport-ping"; | |
71 | case MISC_EPING: | |
72 | case MISC_EPONG: | |
73 | return "encrypted-ping"; | |
74 | default: | |
75 | abort(); | |
76 | } | |
77 | } | |
78 | ||
79 | /* --- @p_ponged@ --- * | |
80 | * | |
81 | * Arguments: @peer *p@ = peer packet arrived from | |
82 | * @unsigned msg@ = message type | |
83 | * @buf *b@ = buffer containing payload | |
84 | * | |
85 | * Returns: --- | |
86 | * | |
87 | * Use: Processes a ping response. | |
88 | */ | |
89 | ||
90 | static void p_ponged(peer *p, unsigned msg, buf *b) | |
91 | { | |
92 | uint32 id; | |
93 | const octet *magic; | |
94 | ping *pg; | |
e04c2d50 | 95 | |
0ba8de86 | 96 | IF_TRACING(T_PEER, { |
97 | trace(T_PEER, "peer: received %s reply from %s", | |
98 | p_pingtype(msg), p->spec.name); | |
99 | trace_block(T_PACKET, "peer: ping contents", BBASE(b), BSZ(b)); | |
100 | }) | |
101 | ||
102 | if (buf_getu32(b, &id) || | |
103 | (magic = buf_get(b, sizeof(pg->magic))) == 0 || | |
104 | BLEFT(b)) { | |
f43df819 | 105 | a_warn("PEER", "?PEER", p, "malformed-%s", p_pingtype(msg), A_END); |
0ba8de86 | 106 | return; |
107 | } | |
108 | ||
109 | for (pg = p->pings; pg; pg = pg->next) { | |
110 | if (pg->id == id) | |
111 | goto found; | |
112 | } | |
f43df819 MW |
113 | a_warn("PEER", |
114 | "?PEER", p, | |
115 | "unexpected-%s", p_pingtype(msg), | |
116 | "0x%08lx", (unsigned long)id, | |
117 | A_END); | |
0ba8de86 | 118 | return; |
119 | ||
120 | found: | |
121 | if (memcmp(magic, pg->magic, sizeof(pg->magic)) != 0) { | |
f43df819 | 122 | a_warn("PEER", "?PEER", p, "corrupt-%s", p_pingtype(msg), A_END); |
0ba8de86 | 123 | return; |
124 | } | |
125 | p_pingdone(pg, PING_OK); | |
126 | } | |
127 | ||
6411163d MW |
128 | /* --- @p_rxupdstats@ --- * |
129 | * | |
130 | * Arguments: @peer *p@ = peer to update | |
131 | * @size_t n@ = size of incoming packet | |
132 | * | |
133 | * Returns: --- | |
134 | * | |
135 | * Use: Updates the peer's incoming packet statistics. | |
136 | */ | |
137 | ||
138 | static void p_rxupdstats(peer *p, size_t n) | |
139 | { | |
140 | p->st.t_last = time(0); | |
141 | p->st.n_in++; | |
142 | p->st.sz_in += n; | |
143 | } | |
144 | ||
a50f9a0e MW |
145 | /* --- @p_encrypt@ --- * |
146 | * | |
147 | * Arguments: @peer *p@ = peer to encrypt message to | |
148 | * @int ty@ message type to send | |
149 | * @buf *bin, *bout@ = input and output buffers | |
150 | * | |
151 | * Returns: --- | |
152 | * | |
153 | * Use: Convenience function for packet encryption. Forces | |
154 | * renegotiation when necessary. Check for the output buffer | |
155 | * being broken to find out whether the encryption was | |
156 | * successful. | |
157 | */ | |
158 | ||
159 | static int p_encrypt(peer *p, int ty, buf *bin, buf *bout) | |
160 | { | |
161 | int err = ksl_encrypt(&p->ks, ty, bin, bout); | |
162 | ||
163 | if (err == KSERR_REGEN) { | |
164 | kx_start(&p->kx, 1); | |
165 | err = 0; | |
166 | } | |
167 | if (!BOK(bout)) | |
168 | err = -1; | |
169 | return (err); | |
170 | } | |
171 | ||
23bbe601 MW |
172 | /* --- @p_updateaddr@ --- * |
173 | * | |
174 | * Arguments: @peer *p@ = pointer to peer block | |
175 | * @const addr *a@ = address to associate with this peer | |
176 | * | |
177 | * Returns: Zero if the address was changed; @+1@ if it was already | |
178 | * right. | |
179 | * | |
180 | * Use: Updates our idea of @p@'s address. | |
181 | */ | |
182 | ||
183 | int p_updateaddr(peer *p, const addr *a) | |
184 | { | |
185 | peer *q; | |
186 | peer_byaddr *pa, *qa; | |
5d06f63e | 187 | int ix; |
23bbe601 MW |
188 | unsigned f; |
189 | ||
190 | /* --- Figure out how to proceed --- * | |
191 | * | |
192 | * If this address already belongs to a different peer, then swap the | |
193 | * addresses over. This doesn't leave the displaced peer in an especially | |
194 | * good state, but it ought to get sorted out soon enough. | |
195 | */ | |
196 | ||
197 | pa = am_find(&byaddr, a, sizeof(peer_byaddr), &f); | |
198 | if (f && pa->p == p) | |
199 | return (+1); | |
200 | else if (!f) { | |
201 | T( trace(T_PEER, "peer: updating address for `%s'", p_name(p)); ) | |
202 | am_remove(&byaddr, p->byaddr); | |
203 | p->byaddr = pa; p->spec.sa = *a; pa->p = p; | |
5d06f63e | 204 | p->afix = afix(p->spec.sa.sa.sa_family); assert(p->afix >= 0); |
23bbe601 MW |
205 | a_notify("NEWADDR", "?PEER", p, "?ADDR", a, A_END); |
206 | return (0); | |
207 | } else { | |
208 | q = pa->p; qa = p->byaddr; | |
209 | T( trace(T_PEER, "peer: swapping addresses for `%s' and `%s'", | |
210 | p_name(p), p_name(q)); ) | |
211 | q->byaddr = qa; qa->p = q; q->spec.sa = p->spec.sa; | |
212 | p->byaddr = pa; pa->p = p; p->spec.sa = *a; | |
5d06f63e | 213 | ix = p->afix; p->afix = q->afix; q->afix = ix; |
23bbe601 MW |
214 | a_notify("NEWADDR", "?PEER", p, "?ADDR", a, A_END); |
215 | a_notify("NEWADDR", "?PEER", q, "?ADDR", &q->spec.sa, A_END); | |
216 | return (0); | |
217 | } | |
218 | } | |
219 | ||
a50f9a0e MW |
220 | /* --- @p_decrypt@ --- * |
221 | * | |
6411163d MW |
222 | * Arguments: @peer **pp@ = pointer to peer to decrypt message from |
223 | * @addr *a@ = address the packet arrived on | |
224 | * @size_t n@ = size of original incoming packet | |
a50f9a0e MW |
225 | * @int ty@ = message type to expect |
226 | * @buf *bin, *bout@ = input and output buffers | |
227 | * | |
228 | * Returns: Zero on success; nonzero on error. | |
229 | * | |
230 | * Use: Convenience function for packet decryption. Reports errors | |
231 | * and updates statistics appropriately. | |
6411163d MW |
232 | * |
233 | * If @*pp@ is null on entry and there are mobile peers then we | |
234 | * see if any of them can decrypt the packet. If so, we record | |
235 | * @*a@ as the peer's new address and send a notification. | |
a50f9a0e MW |
236 | */ |
237 | ||
6411163d MW |
238 | static int p_decrypt(peer **pp, addr *a, size_t n, |
239 | int ty, buf *bin, buf *bout) | |
a50f9a0e | 240 | { |
c71be758 | 241 | peer *p, *q; |
6411163d | 242 | int err = KSERR_DECRYPT; |
6411163d | 243 | |
c71be758 MW |
244 | /* --- If we have a match on the source address then try that first --- */ |
245 | ||
246 | q = *pp; | |
247 | if (q) { | |
6411163d | 248 | T( trace(T_PEER, "peer: decrypting packet from known peer `%s'", |
c71be758 MW |
249 | p_name(q)); ) |
250 | if ((err = ksl_decrypt(&q->ks, ty, bin, bout)) != KSERR_DECRYPT || | |
251 | !(q->spec.f & PSF_MOBILE) || nmobile == 1) { | |
252 | p = q; | |
253 | goto match; | |
254 | } | |
73174919 | 255 | T( trace(T_PEER, "peer: failed to decrypt: try other mobile peers..."); ) |
c71be758 | 256 | } else if (nmobile) |
8ed35e02 | 257 | T( trace(T_PEER, "peer: unknown source: trying mobile peers...") ); |
c71be758 | 258 | else { |
6411163d | 259 | p = 0; |
c71be758 MW |
260 | goto searched; |
261 | } | |
262 | ||
263 | /* --- See whether any mobile peer is interested --- */ | |
264 | ||
01c21903 MW |
265 | p = 0; |
266 | FOREACH_PEER(qq, { | |
267 | if (qq == q || !(qq->spec.f & PSF_MOBILE)) continue; | |
268 | if ((err = ksl_decrypt(&qq->ks, ty, bin, bout)) == KSERR_DECRYPT) { | |
c71be758 | 269 | T( trace(T_PEER, "peer: peer `%s' failed to decrypt", |
01c21903 | 270 | p_name(qq)); ) |
c71be758 MW |
271 | continue; |
272 | } else { | |
01c21903 | 273 | p = qq; |
c71be758 MW |
274 | IF_TRACING(T_PEER, { |
275 | if (!err) | |
01c21903 | 276 | trace(T_PEER, "peer: peer `%s' reports success", p_name(qq)); |
c71be758 MW |
277 | else { |
278 | trace(T_PEER, "peer: peer `%s' reports decryption error %d", | |
01c21903 | 279 | p_name(qq), err); |
6411163d | 280 | } |
c71be758 MW |
281 | }) |
282 | break; | |
6411163d | 283 | } |
c71be758 MW |
284 | }); |
285 | ||
286 | /* --- We've searched the mobile peers --- */ | |
287 | ||
288 | searched: | |
289 | if (!p) { | |
01c21903 MW |
290 | if (!q) |
291 | a_warn("PEER", "-", "unexpected-source", "?ADDR", a, A_END); | |
292 | else { | |
293 | a_warn("PEER", "?PEER", p, "decrypt-failed", | |
294 | "error-code", "%d", err, A_END); | |
295 | p_rxupdstats(q, n); | |
296 | } | |
c71be758 MW |
297 | return (-1); |
298 | } | |
299 | ||
23bbe601 | 300 | /* --- We found one that accepted, so update the peer's address --- */ |
c71be758 MW |
301 | |
302 | if (!err) { | |
01c21903 | 303 | *pp = p; |
23bbe601 | 304 | p_updateaddr(p, a); |
6411163d | 305 | } |
c71be758 MW |
306 | |
307 | match: | |
88622d1d | 308 | p_rxupdstats(p, n); |
6411163d MW |
309 | if (err) { |
310 | if (p) p->st.n_reject++; | |
311 | a_warn("PEER", "?PEER", p, "decrypt-failed", | |
312 | "error-code", "%d", err, A_END); | |
a50f9a0e MW |
313 | return (-1); |
314 | } | |
315 | if (!BOK(bout)) | |
316 | return (-1); | |
317 | return (0); | |
318 | } | |
319 | ||
410c8acf | 320 | /* --- @p_read@ --- * |
321 | * | |
322 | * Arguments: @int fd@ = file descriptor to read from | |
323 | * @unsigned mode@ = what happened | |
324 | * @void *v@ = an uninteresting pointer | |
325 | * | |
326 | * Returns: --- | |
327 | * | |
328 | * Use: Reads a packet from somewhere. | |
329 | */ | |
330 | ||
331 | static void p_read(int fd, unsigned mode, void *v) | |
332 | { | |
37941236 | 333 | peer *p = 0; |
410c8acf | 334 | addr a; |
cb160b86 | 335 | socklen_t sz; |
410c8acf | 336 | ssize_t n; |
337 | int ch; | |
338 | buf b, bb; | |
89640f3f MW |
339 | #ifndef NTRACE |
340 | int ix = -1; | |
341 | char name[NI_MAXHOST], svc[NI_MAXSERV]; | |
342 | #endif | |
410c8acf | 343 | |
344 | /* --- Read the data --- */ | |
345 | ||
f4c9c08f | 346 | QUICKRAND; |
410c8acf | 347 | sz = sizeof(addr); |
348 | n = recvfrom(fd, buf_i, sizeof(buf_i), 0, &a.sa, &sz); | |
349 | if (n < 0) { | |
f43df819 | 350 | a_warn("PEER", "-", "socket-read-error", "?ERRNO", A_END); |
410c8acf | 351 | return; |
352 | } | |
89640f3f MW |
353 | IF_TRACING(T_PEER, { |
354 | ix = afix(a.sa.sa_family); | |
355 | getnameinfo(&a.sa, sz, name, sizeof(name), svc, sizeof(svc), | |
356 | NI_NUMERICHOST | NI_NUMERICSERV); | |
357 | }) | |
410c8acf | 358 | |
37941236 | 359 | /* --- If the packet is a greeting, don't check peers --- */ |
360 | ||
361 | if (n && buf_i[0] == (MSG_MISC | MISC_GREET)) { | |
362 | IF_TRACING(T_PEER, { | |
89640f3f MW |
363 | trace(T_PEER, "peer: greeting received from %s %s %s", |
364 | aftab[ix].name, name, svc); | |
37941236 | 365 | trace_block(T_PACKET, "peer: greeting contents", buf_i, n); |
366 | }) | |
367 | buf_init(&b, buf_i, n); | |
368 | buf_getbyte(&b); | |
369 | if (c_check(&b) || BLEFT(&b)) { | |
f43df819 | 370 | a_warn("PEER", "-", "invalid-greeting", A_END); |
37941236 | 371 | return; |
372 | } | |
f43df819 MW |
373 | a_notify("GREET", |
374 | "?B64", buf_i + 1, (size_t)(n - 1), | |
375 | "?ADDR", &a, | |
376 | A_END); | |
37941236 | 377 | return; |
378 | } | |
379 | ||
6411163d MW |
380 | /* --- Find the appropriate peer --- * |
381 | * | |
382 | * At this stage, don't worry too much about whether we actually found it. | |
383 | */ | |
410c8acf | 384 | |
6411163d | 385 | p = p_findbyaddr(&a); |
410c8acf | 386 | |
b9066fbb | 387 | IF_TRACING(T_PEER, { |
6411163d MW |
388 | if (p) { |
389 | trace(T_PEER, | |
89640f3f MW |
390 | "peer: packet received from `%s' from address %s %s %s", |
391 | p_name(p), aftab[ix].name, name, svc); | |
6411163d | 392 | } else { |
89640f3f MW |
393 | trace(T_PEER, "peer: packet received from unknown address %s %s %s", |
394 | aftab[ix].name, name, svc); | |
6411163d | 395 | } |
b9066fbb | 396 | trace_block(T_PACKET, "peer: packet contents", buf_i, n); |
397 | }) | |
410c8acf | 398 | |
399 | /* --- Pick the packet apart --- */ | |
400 | ||
401 | buf_init(&b, buf_i, n); | |
402 | if ((ch = buf_getbyte(&b)) < 0) { | |
f43df819 | 403 | a_warn("PEER", "?PEER", p, "bad-packet", "no-type", A_END); |
410c8acf | 404 | return; |
405 | } | |
5bb41301 | 406 | switch (ch & MSG_CATMASK) { |
410c8acf | 407 | case MSG_PACKET: |
5bb41301 | 408 | if (ch & MSG_TYPEMASK) { |
f43df819 MW |
409 | a_warn("PEER", |
410 | "?PEER", p, | |
411 | "bad-packet", | |
412 | "unknown-type", "0x%02x", ch, | |
413 | A_END); | |
6411163d | 414 | if (p) p->st.n_reject++; |
5bb41301 | 415 | return; |
416 | } | |
410c8acf | 417 | buf_init(&bb, buf_o, sizeof(buf_o)); |
6411163d | 418 | if (p_decrypt(&p, &a, n, MSG_PACKET, &b, &bb)) |
410c8acf | 419 | return; |
5bb41301 | 420 | if (BOK(&bb)) { |
421 | p->st.n_ipin++; | |
422 | p->st.sz_ipin += BSZ(&b); | |
42da2a58 | 423 | p->t->ops->inject(p->t, &bb); |
5bb41301 | 424 | } else { |
425 | p->st.n_reject++; | |
f43df819 | 426 | a_warn("PEER", "?PEER", p, "packet-build-failed", A_END); |
5bb41301 | 427 | } |
410c8acf | 428 | break; |
5bb41301 | 429 | case MSG_KEYEXCH: |
6411163d | 430 | if (!p) goto unexp; |
88622d1d | 431 | p_rxupdstats(p, n); |
5bb41301 | 432 | kx_message(&p->kx, ch & MSG_TYPEMASK, &b); |
410c8acf | 433 | break; |
0ba8de86 | 434 | case MSG_MISC: |
435 | switch (ch & MSG_TYPEMASK) { | |
436 | case MISC_NOP: | |
6411163d | 437 | if (!p) goto unexp; |
88622d1d | 438 | p_rxupdstats(p, n); |
0ba8de86 | 439 | T( trace(T_PEER, "peer: received NOP packet"); ) |
440 | break; | |
441 | case MISC_PING: | |
6411163d | 442 | if (!p) goto unexp; |
88622d1d | 443 | p_rxupdstats(p, n); |
0ba8de86 | 444 | buf_put(p_txstart(p, MSG_MISC | MISC_PONG), BCUR(&b), BLEFT(&b)); |
445 | p_txend(p); | |
e04c2d50 | 446 | break; |
0ba8de86 | 447 | case MISC_PONG: |
6411163d | 448 | if (!p) goto unexp; |
88622d1d | 449 | p_rxupdstats(p, n); |
0ba8de86 | 450 | p_ponged(p, MISC_PONG, &b); |
451 | break; | |
452 | case MISC_EPING: | |
453 | buf_init(&bb, buf_t, sizeof(buf_t)); | |
6411163d | 454 | if (p_decrypt(&p, &a, n, ch, &b, &bb)) |
0ba8de86 | 455 | return; |
0ba8de86 | 456 | if (BOK(&bb)) { |
457 | buf_flip(&bb); | |
a50f9a0e MW |
458 | p_encrypt(p, MSG_MISC | MISC_EPONG, &bb, |
459 | p_txstart(p, MSG_MISC | MISC_EPONG)); | |
0ba8de86 | 460 | p_txend(p); |
461 | } | |
462 | break; | |
463 | case MISC_EPONG: | |
464 | buf_init(&bb, buf_t, sizeof(buf_t)); | |
6411163d | 465 | if (p_decrypt(&p, &a, n, ch, &b, &bb)) |
0ba8de86 | 466 | return; |
0ba8de86 | 467 | if (BOK(&bb)) { |
468 | buf_flip(&bb); | |
469 | p_ponged(p, MISC_EPONG, &bb); | |
470 | } | |
471 | break; | |
472 | } | |
473 | break; | |
410c8acf | 474 | default: |
6411163d | 475 | if (p) p->st.n_reject++; |
f43df819 MW |
476 | a_warn("PEER", |
477 | "?PEER", p, | |
478 | "bad-packet", | |
a68280fc | 479 | "unknown-category", "0x%02x", ch, |
f43df819 | 480 | A_END); |
410c8acf | 481 | break; |
6411163d MW |
482 | unexp: |
483 | a_warn("PEER", "-", "unexpected-source", "?ADDR", &a, A_END); | |
484 | break; | |
410c8acf | 485 | } |
486 | } | |
487 | ||
488 | /* --- @p_txstart@ --- * | |
489 | * | |
490 | * Arguments: @peer *p@ = pointer to peer block | |
491 | * @unsigned msg@ = message type code | |
492 | * | |
493 | * Returns: A pointer to a buffer to write to. | |
494 | * | |
495 | * Use: Starts sending to a peer. Only one send can happen at a | |
496 | * time. | |
497 | */ | |
498 | ||
499 | buf *p_txstart(peer *p, unsigned msg) | |
500 | { | |
501 | buf_init(&p->b, buf_o, sizeof(buf_o)); | |
502 | buf_putbyte(&p->b, msg); | |
503 | return (&p->b); | |
504 | } | |
505 | ||
506 | /* --- @p_txend@ --- * | |
507 | * | |
508 | * Arguments: @peer *p@ = pointer to peer block | |
509 | * | |
510 | * Returns: --- | |
511 | * | |
512 | * Use: Sends a packet to the peer. | |
513 | */ | |
514 | ||
0ba8de86 | 515 | static void p_setkatimer(peer *); |
516 | ||
517 | static int p_dotxend(peer *p) | |
410c8acf | 518 | { |
cb2c2bfc MW |
519 | socklen_t sasz = addrsz(&p->spec.sa); |
520 | ||
410c8acf | 521 | if (!BOK(&p->b)) { |
f43df819 | 522 | a_warn("PEER", "?PEER", p, "packet-build-failed", A_END); |
0ba8de86 | 523 | return (0); |
410c8acf | 524 | } |
525 | IF_TRACING(T_PEER, trace_block(T_PACKET, "peer: sending packet", | |
526 | BBASE(&p->b), BLEN(&p->b)); ) | |
5d06f63e | 527 | if (sendto(udpsock[p->afix].fd, BBASE(&p->b), BLEN(&p->b), |
cb2c2bfc | 528 | 0, &p->spec.sa.sa, sasz) < 0) { |
f43df819 | 529 | a_warn("PEER", "?PEER", p, "socket-write-error", "?ERRNO", A_END); |
0ba8de86 | 530 | return (0); |
531 | } else { | |
5bb41301 | 532 | p->st.n_out++; |
533 | p->st.sz_out += BLEN(&p->b); | |
0ba8de86 | 534 | return (1); |
5bb41301 | 535 | } |
410c8acf | 536 | } |
537 | ||
0ba8de86 | 538 | void p_txend(peer *p) |
539 | { | |
540 | if (p_dotxend(p) && p->spec.t_ka) { | |
541 | sel_rmtimer(&p->tka); | |
542 | p_setkatimer(p); | |
543 | } | |
544 | } | |
545 | ||
546 | /* --- @p_pingwrite@ --- * | |
547 | * | |
548 | * Arguments: @ping *p@ = ping structure | |
549 | * @buf *b@ = buffer to write in | |
550 | * | |
551 | * Returns: --- | |
552 | * | |
553 | * Use: Fills in a ping structure and writes the packet payload. | |
554 | */ | |
555 | ||
556 | static void p_pingwrite(ping *p, buf *b) | |
557 | { | |
558 | static uint32 seq = 0; | |
559 | ||
560 | p->id = U32(seq++); | |
561 | GR_FILL(&rand_global, p->magic, sizeof(p->magic)); | |
562 | buf_putu32(b, p->id); | |
563 | buf_put(b, p->magic, sizeof(p->magic)); | |
564 | } | |
565 | ||
566 | /* --- @p_pingdone@ --- * | |
567 | * | |
568 | * Arguments: @ping *p@ = ping structure | |
569 | * @int rc@ = return code to pass on | |
570 | * | |
571 | * Returns: --- | |
572 | * | |
573 | * Use: Disposes of a ping structure, maybe sending a notification. | |
574 | */ | |
575 | ||
576 | void p_pingdone(ping *p, int rc) | |
577 | { | |
0ba8de86 | 578 | if (p->prev) p->prev->next = p->next; |
579 | else p->p->pings = p->next; | |
580 | if (p->next) p->next->prev = p->prev; | |
581 | if (rc != PING_TIMEOUT) sel_rmtimer(&p->t); | |
060ca767 | 582 | T( trace(T_PEER, "peer: ping 0x%08lx done (rc = %d)", |
583 | (unsigned long)p->id, rc); ) | |
0ba8de86 | 584 | if (rc >= 0) p->func(rc, p->arg); |
585 | } | |
586 | ||
587 | /* --- @p_pingtimeout@ --- * | |
588 | * | |
589 | * Arguments: @struct timeval *now@ = the time now | |
590 | * @void *pv@ = pointer to ping block | |
591 | * | |
592 | * Returns: --- | |
593 | * | |
594 | * Use: Called when a ping times out. | |
595 | */ | |
596 | ||
597 | static void p_pingtimeout(struct timeval *now, void *pv) | |
598 | { | |
599 | ping *p = pv; | |
600 | ||
601 | T( trace(T_PEER, "peer: ping 0x%08lx timed out", (unsigned long)p->id); ) | |
602 | p_pingdone(p, PING_TIMEOUT); | |
603 | } | |
604 | ||
605 | /* --- @p_pingsend@ --- * | |
606 | * | |
607 | * Arguments: @peer *p@ = destination peer | |
608 | * @ping *pg@ = structure to fill in | |
609 | * @unsigned type@ = message type | |
610 | * @unsigned long timeout@ = how long to wait before giving up | |
611 | * @void (*func)(int, void *)@ = callback function | |
612 | * @void *arg@ = argument for callback | |
613 | * | |
614 | * Returns: Zero if successful, nonzero if it failed. | |
615 | * | |
616 | * Use: Sends a ping to a peer. Call @func@ with a nonzero argument | |
617 | * if we get an answer within the timeout, or zero if no answer. | |
618 | */ | |
619 | ||
620 | int p_pingsend(peer *p, ping *pg, unsigned type, | |
621 | unsigned long timeout, | |
622 | void (*func)(int, void *), void *arg) | |
623 | { | |
624 | buf *b, bb; | |
625 | struct timeval tv; | |
626 | ||
0ba8de86 | 627 | switch (type) { |
628 | case MISC_PING: | |
629 | pg->msg = MISC_PONG; | |
630 | b = p_txstart(p, MSG_MISC | MISC_PING); | |
631 | p_pingwrite(pg, b); | |
632 | p_txend(p); | |
633 | break; | |
634 | case MISC_EPING: | |
635 | pg->msg = MISC_EPONG; | |
636 | b = p_txstart(p, MSG_MISC | MISC_EPING); | |
637 | buf_init(&bb, buf_t, sizeof(buf_t)); | |
638 | p_pingwrite(pg, &bb); | |
639 | buf_flip(&bb); | |
a50f9a0e | 640 | p_encrypt(p, MSG_MISC | MISC_EPING, &bb, b); |
0ba8de86 | 641 | if (!BOK(b)) |
642 | return (-1); | |
643 | p_txend(p); | |
644 | break; | |
645 | default: | |
646 | abort(); | |
647 | break; | |
648 | } | |
649 | ||
650 | pg->next = p->pings; | |
651 | pg->prev = 0; | |
652 | pg->p = p; | |
653 | pg->func = func; | |
654 | pg->arg = arg; | |
060ca767 | 655 | if (p->pings) p->pings->prev = pg; |
0ba8de86 | 656 | p->pings = pg; |
657 | gettimeofday(&tv, 0); | |
658 | tv.tv_sec += timeout; | |
659 | sel_addtimer(&sel, &pg->t, &tv, p_pingtimeout, pg); | |
660 | T( trace(T_PEER, "peer: send %s 0x%08lx to %s", | |
661 | p_pingtype(type), (unsigned long)pg->id, p->spec.name); ) | |
662 | return (0); | |
663 | } | |
664 | ||
37941236 | 665 | /* --- @p_greet@ --- * |
666 | * | |
667 | * Arguments: @peer *p@ = peer to send to | |
668 | * @const void *c@ = pointer to challenge | |
669 | * @size_t sz@ = size of challenge | |
670 | * | |
671 | * Returns: --- | |
672 | * | |
673 | * Use: Sends a greeting packet. | |
674 | */ | |
675 | ||
676 | void p_greet(peer *p, const void *c, size_t sz) | |
677 | { | |
678 | buf *b = p_txstart(p, MSG_MISC | MISC_GREET); | |
679 | buf_put(b, c, sz); | |
680 | p_txend(p); | |
681 | } | |
682 | ||
410c8acf | 683 | /* --- @p_tun@ --- * |
684 | * | |
685 | * Arguments: @peer *p@ = pointer to peer block | |
686 | * @buf *b@ = buffer containing incoming packet | |
687 | * | |
688 | * Returns: --- | |
689 | * | |
690 | * Use: Handles a packet which needs to be sent to a peer. | |
691 | */ | |
692 | ||
693 | void p_tun(peer *p, buf *b) | |
694 | { | |
695 | buf *bb = p_txstart(p, MSG_PACKET); | |
7b052fe6 | 696 | |
f4c9c08f | 697 | QUICKRAND; |
a50f9a0e | 698 | p_encrypt(p, MSG_PACKET, b, bb); |
5bb41301 | 699 | if (BOK(bb) && BLEN(bb)) { |
700 | p->st.n_ipout++; | |
701 | p->st.sz_ipout += BLEN(bb); | |
410c8acf | 702 | p_txend(p); |
5bb41301 | 703 | } |
410c8acf | 704 | } |
705 | ||
de014da6 | 706 | /* --- @p_keyreload@ --- * |
707 | * | |
708 | * Arguments: --- | |
709 | * | |
710 | * Returns: --- | |
711 | * | |
712 | * Use: Forces a check of the daemon's keyring files. | |
713 | */ | |
714 | ||
715 | void p_keyreload(void) | |
716 | { | |
c8e02c8a MW |
717 | if (km_reload()) |
718 | FOREACH_PEER(p, { kx_newkeys(&p->kx); }); | |
de014da6 | 719 | } |
720 | ||
410c8acf | 721 | /* --- @p_interval@ --- * |
722 | * | |
723 | * Arguments: --- | |
724 | * | |
725 | * Returns: --- | |
726 | * | |
727 | * Use: Called periodically to do tidying. | |
728 | */ | |
729 | ||
730 | void p_interval(void) | |
731 | { | |
de014da6 | 732 | p_keyreload(); |
c8e02c8a | 733 | FOREACH_PEER(p, { ksl_prune(&p->ks); }); |
410c8acf | 734 | } |
735 | ||
5bb41301 | 736 | /* --- @p_stats@ --- * |
737 | * | |
738 | * Arguments: @peer *p@ = pointer to a peer block | |
739 | * | |
740 | * Returns: A pointer to the peer's statistics. | |
741 | */ | |
742 | ||
743 | stats *p_stats(peer *p) { return (&p->st); } | |
744 | ||
410c8acf | 745 | /* --- @p_ifname@ --- * |
746 | * | |
747 | * Arguments: @peer *p@ = pointer to a peer block | |
748 | * | |
749 | * Returns: A pointer to the peer's interface name. | |
750 | */ | |
751 | ||
64cf2223 MW |
752 | const char *p_ifname(peer *p) { return (p->ifname); } |
753 | ||
754 | /* --- @p_setifname@ --- * | |
755 | * | |
756 | * Arguments: @peer *p@ = pointer to a peer block | |
757 | * @const char *name@ = pointer to the new name | |
758 | * | |
759 | * Returns: --- | |
760 | * | |
761 | * Use: Changes the name held for a peer's interface. | |
762 | */ | |
763 | ||
764 | void p_setifname(peer *p, const char *name) | |
72917fe7 MW |
765 | { |
766 | xfree(p->ifname); | |
767 | p->ifname = xstrdup(name); | |
768 | if (p->spec.tops->setifname) | |
769 | p->spec.tops->setifname(p->t, name); | |
770 | } | |
410c8acf | 771 | |
772 | /* --- @p_addr@ --- * | |
773 | * | |
774 | * Arguments: @peer *p@ = pointer to a peer block | |
775 | * | |
776 | * Returns: A pointer to the peer's address. | |
777 | */ | |
778 | ||
0ba8de86 | 779 | const addr *p_addr(peer *p) { return (&p->spec.sa); } |
410c8acf | 780 | |
781 | /* --- @p_init@ --- * | |
782 | * | |
89640f3f | 783 | * Arguments: @struct addrinfo *ailist@ = addresses to bind to |
410c8acf | 784 | * |
785 | * Returns: --- | |
786 | * | |
787 | * Use: Initializes the peer system; creates the socket. | |
788 | */ | |
789 | ||
89640f3f | 790 | void p_init(struct addrinfo *ailist) |
410c8acf | 791 | { |
792 | int fd; | |
df9dfccf | 793 | int len = PKBUFSZ; |
47828bd9 | 794 | int yes = 1; |
89640f3f MW |
795 | int i; |
796 | struct addrinfo *ai; | |
797 | unsigned port, lastport = 0; | |
798 | addr a; | |
799 | socklen_t sz; | |
0a9920e2 | 800 | |
89640f3f MW |
801 | for (i = 0; i < NADDRFAM; i++) udpsock[i].fd = -1; |
802 | ||
803 | for (ai = ailist; ai; ai = ai->ai_next) { | |
804 | if ((i = afix(ai->ai_family)) < 0) continue; | |
805 | if (udpsock[i].fd != -1) continue; | |
806 | ||
807 | /* --- Note on socket buffer sizes --- * | |
808 | * | |
809 | * For some bizarre reason, Linux 2.2 (at least) doubles the socket | |
810 | * buffer sizes I pass to @setsockopt@. I'm not putting special-case | |
811 | * code here for Linux: BSD (at least TCPv2) does what I tell it rather | |
812 | * than second-guessing me. | |
813 | */ | |
814 | ||
815 | if ((fd = socket(ai->ai_family, SOCK_DGRAM, 0)) < 0) | |
816 | die(EXIT_FAILURE, "socket creation failed: %s", strerror(errno)); | |
47828bd9 MW |
817 | if (i == AFIX_INET6 && |
818 | setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes))) { | |
819 | die(EXIT_FAILURE, "failed to set IPv6-only state: %s", | |
820 | strerror(errno)); | |
821 | } | |
89640f3f MW |
822 | assert(ai->ai_addrlen <= sizeof(a)); |
823 | memcpy(&a, ai->ai_addr, ai->ai_addrlen); | |
824 | if ((port = getport(&a)) == 0 && lastport) setport(&a, lastport); | |
825 | if (bind(fd, &a.sa, addrsz(&a))) | |
826 | die(EXIT_FAILURE, "bind failed: %s", strerror(errno)); | |
827 | if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, sizeof(len)) || | |
828 | setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &len, sizeof(len))) { | |
829 | die(EXIT_FAILURE, "failed to set socket buffer sizes: %s", | |
830 | strerror(errno)); | |
831 | } | |
832 | fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC); | |
833 | sel_initfile(&sel, &udpsock[i], fd, SEL_READ, p_read, 0); | |
834 | sel_addfile(&udpsock[i]); | |
835 | T( trace(T_PEER, "peer: created %s socket", aftab[i].name); ) | |
836 | if (!port) { | |
837 | sz = sizeof(a); | |
838 | if (getsockname(fd, &a.sa, &sz)) { | |
839 | die(EXIT_FAILURE, "failed to read local socket address: %s", | |
840 | strerror(errno)); | |
841 | } | |
842 | lastport = getport(&a); | |
843 | } | |
0a9920e2 | 844 | } |
c8e02c8a MW |
845 | |
846 | sym_create(&byname); | |
847 | am_create(&byaddr); | |
410c8acf | 848 | } |
849 | ||
850 | /* --- @p_port@ --- * | |
851 | * | |
5d06f63e | 852 | * Arguments: @int i@ = address family index to retrieve |
410c8acf | 853 | * |
854 | * Returns: Port number used for socket. | |
855 | */ | |
856 | ||
5d06f63e | 857 | unsigned p_port(int i) |
410c8acf | 858 | { |
859 | addr a; | |
cb160b86 | 860 | socklen_t sz = sizeof(addr); |
410c8acf | 861 | |
5d06f63e | 862 | if (getsockname(udpsock[i].fd, &a.sa, &sz)) |
410c8acf | 863 | die(EXIT_FAILURE, "couldn't read port number: %s", strerror(errno)); |
d98625f4 | 864 | return (getport(&a)); |
410c8acf | 865 | } |
866 | ||
0ba8de86 | 867 | /* --- @p_keepalive@ --- * |
868 | * | |
869 | * Arguments: @struct timeval *now@ = the current time | |
870 | * @void *pv@ = peer to wake up | |
871 | * | |
872 | * Returns: --- | |
873 | * | |
874 | * Use: Sends a keepalive ping message to its peer. | |
875 | */ | |
876 | ||
877 | static void p_keepalive(struct timeval *now, void *pv) | |
878 | { | |
879 | peer *p = pv; | |
fe2a5dcf | 880 | |
0ba8de86 | 881 | p_txstart(p, MSG_MISC | MISC_NOP); p_dotxend(p); |
882 | T( trace(T_PEER, "peer: sent keepalive to %s", p->spec.name); ) | |
883 | p_setkatimer(p); | |
884 | } | |
885 | ||
886 | /* --- @p_setkatimer@ --- * | |
887 | * | |
888 | * Arguments: @peer *p@ = peer to set | |
889 | * | |
890 | * Returns: --- | |
891 | * | |
892 | * Use: Resets the keepalive timer thing. | |
893 | */ | |
894 | ||
895 | static void p_setkatimer(peer *p) | |
896 | { | |
897 | struct timeval tv; | |
898 | ||
899 | if (!p->spec.t_ka) | |
900 | return; | |
901 | gettimeofday(&tv, 0); | |
902 | tv.tv_sec += p->spec.t_ka; | |
903 | sel_addtimer(&sel, &p->tka, &tv, p_keepalive, p); | |
904 | } | |
905 | ||
410c8acf | 906 | /* --- @p_create@ --- * |
907 | * | |
0ba8de86 | 908 | * Arguments: @peerspec *spec@ = information about this peer |
410c8acf | 909 | * |
910 | * Returns: Pointer to the peer block, or null if it failed. | |
911 | * | |
912 | * Use: Creates a new named peer block. No peer is actually attached | |
913 | * by this point. | |
914 | */ | |
915 | ||
0ba8de86 | 916 | peer *p_create(peerspec *spec) |
410c8acf | 917 | { |
918 | peer *p = CREATE(peer); | |
eb5f3fea MW |
919 | const tunnel_ops *tops = spec->tops; |
920 | int fd; | |
c8e02c8a MW |
921 | unsigned f; |
922 | ||
923 | p->byname = sym_find(&byname, spec->name, -1, sizeof(peer_byname), &f); | |
924 | if (f) goto tidy_0; | |
925 | p->byaddr = am_find(&byaddr, &spec->sa, sizeof(peer_byaddr), &f); | |
926 | if (f) goto tidy_1; | |
927 | p->byname->p = p->byaddr->p = p; | |
42da2a58 | 928 | |
0ba8de86 | 929 | T( trace(T_PEER, "peer: creating new peer `%s'", spec->name); ) |
930 | p->spec = *spec; | |
c8e02c8a | 931 | p->spec.name = (/*unconst*/ char *)SYM_NAME(p->byname); |
fe2a5dcf MW |
932 | if (spec->tag) p->spec.tag = xstrdup(spec->tag); |
933 | if (spec->privtag) p->spec.privtag = xstrdup(spec->privtag); | |
410c8acf | 934 | p->ks = 0; |
629842d8 | 935 | p->pings = 0; |
64cf2223 | 936 | p->ifname = 0; |
5d06f63e | 937 | p->afix = afix(p->spec.sa.sa.sa_family); assert(p->afix >= 0); |
5bb41301 | 938 | memset(&p->st, 0, sizeof(stats)); |
939 | p->st.t_start = time(0); | |
388e0319 | 940 | if (!(tops->flags & TUNF_PRIVOPEN)) |
eb5f3fea | 941 | fd = -1; |
388e0319 | 942 | else if ((fd = ps_tunfd(tops, &p->ifname)) < 0) |
c8e02c8a | 943 | goto tidy_2; |
eb5f3fea MW |
944 | if ((p->t = tops->create(p, fd, &p->ifname)) == 0) |
945 | goto tidy_3; | |
946 | T( trace(T_TUNNEL, "peer: attached interface %s to peer `%s'", | |
947 | p->ifname, p_name(p)); ) | |
0ba8de86 | 948 | p_setkatimer(p); |
8743c776 | 949 | if (kx_init(&p->kx, p, &p->ks, p->spec.f & PSF_KXMASK)) |
eb5f3fea | 950 | goto tidy_4; |
f43df819 | 951 | a_notify("ADD", |
6047fbac MW |
952 | "?PEER", p, |
953 | "%s", p->ifname, | |
954 | "?ADDR", &p->spec.sa, | |
955 | A_END); | |
8743c776 | 956 | if (!(p->spec.f & KXF_CORK)) { |
010e6f63 | 957 | a_notify("KXSTART", "?PEER", p, A_END); |
f43df819 | 958 | /* Couldn't tell anyone before */ |
010e6f63 | 959 | } |
6411163d | 960 | if (p->spec.f & PSF_MOBILE) nmobile++; |
410c8acf | 961 | return (p); |
962 | ||
eb5f3fea | 963 | tidy_4: |
fe2a5dcf | 964 | if (spec->t_ka) sel_rmtimer(&p->tka); |
72917fe7 | 965 | xfree(p->ifname); |
0ba8de86 | 966 | p->t->ops->destroy(p->t); |
eb5f3fea MW |
967 | tidy_3: |
968 | if (fd >= 0) close(fd); | |
c8e02c8a MW |
969 | tidy_2: |
970 | am_remove(&byaddr, p->byaddr); | |
48b84569 | 971 | if (p->spec.tag) xfree(p->spec.tag); |
fe2a5dcf | 972 | if (p->spec.privtag) xfree(p->spec.privtag); |
c8e02c8a MW |
973 | tidy_1: |
974 | sym_remove(&byname, p->byname); | |
410c8acf | 975 | tidy_0: |
410c8acf | 976 | DESTROY(p); |
977 | return (0); | |
978 | } | |
979 | ||
980 | /* --- @p_name@ --- * | |
981 | * | |
982 | * Arguments: @peer *p@ = pointer to a peer block | |
983 | * | |
984 | * Returns: A pointer to the peer's name. | |
985 | */ | |
986 | ||
6411163d MW |
987 | const char *p_name(peer *p) |
988 | { if (p) return (p->spec.name); else return ("-"); } | |
410c8acf | 989 | |
48b84569 MW |
990 | /* --- @p_tag@ --- * |
991 | * | |
992 | * Arguments: @peer *p@ = pointer to a peer block | |
993 | * | |
994 | * Returns: A pointer to the peer's public key tag. | |
995 | */ | |
996 | ||
997 | const char *p_tag(peer *p) | |
998 | { return (p->spec.tag ? p->spec.tag : p->spec.name); } | |
999 | ||
fe2a5dcf MW |
1000 | /* --- @p_privtag@ --- * |
1001 | * | |
1002 | * Arguments: @peer *p@ = pointer to a peer block | |
1003 | * | |
1004 | * Returns: A pointer to the peer's private key tag. | |
1005 | */ | |
1006 | ||
1007 | const char *p_privtag(peer *p) | |
1008 | { return (p->spec.privtag ? p->spec.privtag : tag_priv); } | |
1009 | ||
060ca767 | 1010 | /* --- @p_spec@ --- * |
1011 | * | |
1012 | * Arguments: @peer *p@ = pointer to a peer block | |
1013 | * | |
1014 | * Returns: Pointer to the peer's specification | |
1015 | */ | |
1016 | ||
1017 | const peerspec *p_spec(peer *p) { return (&p->spec); } | |
1018 | ||
c8e02c8a MW |
1019 | /* --- @p_findbyaddr@ --- * |
1020 | * | |
1021 | * Arguments: @const addr *a@ = address to look up | |
1022 | * | |
1023 | * Returns: Pointer to the peer block, or null if not found. | |
1024 | * | |
1025 | * Use: Finds a peer by address. | |
1026 | */ | |
1027 | ||
1028 | peer *p_findbyaddr(const addr *a) | |
1029 | { | |
1030 | peer_byaddr *pa; | |
1031 | ||
6411163d MW |
1032 | if ((pa = am_find(&byaddr, a, 0, 0)) != 0) { |
1033 | assert(pa->p); | |
c8e02c8a | 1034 | return (pa->p); |
6411163d | 1035 | } |
c8e02c8a MW |
1036 | return (0); |
1037 | } | |
1038 | ||
410c8acf | 1039 | /* --- @p_find@ --- * |
1040 | * | |
1041 | * Arguments: @const char *name@ = name to look up | |
1042 | * | |
1043 | * Returns: Pointer to the peer block, or null if not found. | |
1044 | * | |
1045 | * Use: Finds a peer by name. | |
1046 | */ | |
1047 | ||
1048 | peer *p_find(const char *name) | |
1049 | { | |
c8e02c8a MW |
1050 | peer_byname *pn; |
1051 | ||
1052 | if ((pn = sym_find(&byname, name, -1, 0, 0)) != 0) | |
1053 | return (pn->p); | |
e04c2d50 | 1054 | return (0); |
410c8acf | 1055 | } |
1056 | ||
1057 | /* --- @p_destroy@ --- * | |
1058 | * | |
1059 | * Arguments: @peer *p@ = pointer to a peer | |
1060 | * | |
1061 | * Returns: --- | |
1062 | * | |
1063 | * Use: Destroys a peer. | |
1064 | */ | |
1065 | ||
1066 | void p_destroy(peer *p) | |
1067 | { | |
0ba8de86 | 1068 | ping *pg, *ppg; |
1069 | ||
1070 | T( trace(T_PEER, "peer: destroying peer `%s'", p->spec.name); ) | |
f43df819 | 1071 | a_notify("KILL", "%s", p->spec.name, A_END); |
5bb41301 | 1072 | ksl_free(&p->ks); |
410c8acf | 1073 | kx_free(&p->kx); |
fe2a5dcf MW |
1074 | if (p->spec.f & PSF_MOBILE) nmobile--; |
1075 | if (p->ifname) xfree(p->ifname); | |
1076 | if (p->spec.tag) xfree(p->spec.tag); | |
1077 | if (p->spec.privtag) xfree(p->spec.privtag); | |
42da2a58 | 1078 | p->t->ops->destroy(p->t); |
fe2a5dcf | 1079 | if (p->spec.t_ka) sel_rmtimer(&p->tka); |
0ba8de86 | 1080 | for (pg = p->pings; pg; pg = ppg) { |
1081 | ppg = pg->next; | |
1082 | p_pingdone(pg, PING_PEERDIED); | |
1083 | } | |
c8e02c8a MW |
1084 | sym_remove(&byname, p->byname); |
1085 | am_remove(&byaddr, p->byaddr); | |
410c8acf | 1086 | DESTROY(p); |
1087 | } | |
1088 | ||
c8e02c8a MW |
1089 | /* --- @p_mkiter@ --- * |
1090 | * | |
1091 | * Arguments: @peer_iter *i@ = pointer to an iterator | |
1092 | * | |
1093 | * Returns: --- | |
1094 | * | |
1095 | * Use: Initializes the iterator. | |
1096 | */ | |
1097 | ||
1098 | void p_mkiter(peer_iter *i) { sym_mkiter(&i->i, &byname); } | |
1099 | ||
1100 | /* --- @p_next@ --- * | |
1101 | * | |
1102 | * Arguments: @peer_iter *i@ = pointer to an iterator | |
410c8acf | 1103 | * |
c8e02c8a | 1104 | * Returns: Next peer, or null if at the end. |
410c8acf | 1105 | * |
c8e02c8a | 1106 | * Use: Returns the next peer. |
410c8acf | 1107 | */ |
1108 | ||
c8e02c8a MW |
1109 | peer *p_next(peer_iter *i) |
1110 | { | |
1111 | peer_byname *pn; | |
1112 | ||
1113 | if ((pn = sym_next(&i->i)) == 0) | |
1114 | return (0); | |
1115 | return (pn->p); | |
1116 | } | |
410c8acf | 1117 | |
1118 | /*----- That's all, folks -------------------------------------------------*/ |