X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/tripe/blobdiff_plain/eb5f3fea8a65306e424a353951208b11bd3aac22..62b5e3ecc77424add00ad4e5dc86e7248751cdf6:/server/peer.c diff --git a/server/peer.c b/server/peer.c index 3cb12ea2..72287cdd 100644 --- a/server/peer.c +++ b/server/peer.c @@ -33,6 +33,7 @@ static sym_table byname; static addrmap byaddr; static sel_file sock; +static unsigned nmobile; /*----- Tunnel table ------------------------------------------------------*/ @@ -122,6 +123,23 @@ found: p_pingdone(pg, PING_OK); } +/* --- @p_rxupdstats@ --- * + * + * Arguments: @peer *p@ = peer to update + * @size_t n@ = size of incoming packet + * + * Returns: --- + * + * Use: Updates the peer's incoming packet statistics. + */ + +static void p_rxupdstats(peer *p, size_t n) +{ + p->st.t_last = time(0); + p->st.n_in++; + p->st.sz_in += n; +} + /* --- @p_encrypt@ --- * * * Arguments: @peer *p@ = peer to encrypt message to @@ -151,7 +169,9 @@ static int p_encrypt(peer *p, int ty, buf *bin, buf *bout) /* --- @p_decrypt@ --- * * - * Arguments: @peer *p@ = peer to decrypt message from + * Arguments: @peer **pp@ = pointer to peer to decrypt message from + * @addr *a@ = address the packet arrived on + * @size_t n@ = size of original incoming packet * @int ty@ = message type to expect * @buf *bin, *bout@ = input and output buffers * @@ -159,13 +179,67 @@ static int p_encrypt(peer *p, int ty, buf *bin, buf *bout) * * Use: Convenience function for packet decryption. Reports errors * and updates statistics appropriately. + * + * If @*pp@ is null on entry and there are mobile peers then we + * see if any of them can decrypt the packet. If so, we record + * @*a@ as the peer's new address and send a notification. */ -static int p_decrypt(peer *p, int ty, buf *bin, buf *bout) +static int p_decrypt(peer **pp, addr *a, size_t n, + int ty, buf *bin, buf *bout) { - if (ksl_decrypt(&p->ks, ty, bin, bout)) { - p->st.n_reject++; - a_warn("PEER", "?PEER", p, "decrypt-failed", A_END); + peer *p; + peer_byaddr *pa; + int err = KSERR_DECRYPT; + unsigned f; + + if (*pp) { + p = *pp; + T( trace(T_PEER, "peer: decrypting packet from known peer `%s'", + p_name(p)); ) + err = ksl_decrypt(&p->ks, ty, bin, bout); + } else { + p = 0; + if (nmobile) { + T( trace(T_PEER, "peer: unknown source: trying mobile peers..."); ) + FOREACH_PEER(q, { + if ((err = ksl_decrypt(&q->ks, ty, bin, bout)) == KSERR_DECRYPT) { + T( trace(T_PEER, "peer: peer `%s' failed to decrypt", + p_name(q)); ) + continue; + } else { + p = *pp = q; + IF_TRACING(T_PEER, { + if (!err) + trace(T_PEER, "peer: peer `%s' reports success", p_name(p)); + else { + trace(T_PEER, "peer: peer `%s' reports decryption error %d", + p_name(p), err); + } + }) + break; + } + }); + } + if (!p) { + a_warn("PEER", "-", "unexpected-source", "?ADDR", a, A_END); + return (-1); + } + if (!err) { + T( trace(T_PEER, "peer: updating address for `%s'", p_name(p)); ) + p_rxupdstats(p, n); + pa = am_find(&byaddr, a, sizeof(peer_byaddr), &f); assert(!f); + am_remove(&byaddr, p->byaddr); + p->byaddr = pa; + pa->p = p; + p->spec.sa = *a; + a_notify("NEWADDR", "?PEER", p, "?ADDR", a, A_END); + } + } + if (err) { + if (p) p->st.n_reject++; + a_warn("PEER", "?PEER", p, "decrypt-failed", + "error-code", "%d", err, A_END); return (-1); } if (!BOK(bout)) @@ -225,23 +299,28 @@ static void p_read(int fd, unsigned mode, void *v) return; } - /* --- Find the appropriate peer --- */ + /* --- Find the appropriate peer --- * + * + * At this stage, don't worry too much about whether we actually found it. + */ - if ((p = p_findbyaddr(&a)) == 0) { - a_warn("PEER", "-", "unexpected-source", "?ADDR", &a, A_END); - return; - } + p = p_findbyaddr(&a); IF_TRACING(T_PEER, { - trace(T_PEER, "peer: packet received from `%s'", p->spec.name); + if (p) { + trace(T_PEER, + "peer: packet received from `%s' from address INET %s %d", + p_name(p), inet_ntoa(a.sin.sin_addr), ntohs(a.sin.sin_port)); + } else { + trace(T_PEER, "peer: packet received from unknown address INET %s %d", + inet_ntoa(a.sin.sin_addr), ntohs(a.sin.sin_port)); + } trace_block(T_PACKET, "peer: packet contents", buf_i, n); }) /* --- Pick the packet apart --- */ - p->st.t_last = time(0); - p->st.n_in++; - p->st.sz_in += n; + if (p) p_rxupdstats(p, n); buf_init(&b, buf_i, n); if ((ch = buf_getbyte(&b)) < 0) { a_warn("PEER", "?PEER", p, "bad-packet", "no-type", A_END); @@ -255,11 +334,11 @@ static void p_read(int fd, unsigned mode, void *v) "bad-packet", "unknown-type", "0x%02x", ch, A_END); - p->st.n_reject++; + if (p) p->st.n_reject++; return; } buf_init(&bb, buf_o, sizeof(buf_o)); - if (p_decrypt(p, MSG_PACKET, &b, &bb)) + if (p_decrypt(&p, &a, n, MSG_PACKET, &b, &bb)) return; if (BOK(&bb)) { p->st.n_ipin++; @@ -271,23 +350,27 @@ static void p_read(int fd, unsigned mode, void *v) } break; case MSG_KEYEXCH: + if (!p) goto unexp; kx_message(&p->kx, ch & MSG_TYPEMASK, &b); break; case MSG_MISC: switch (ch & MSG_TYPEMASK) { case MISC_NOP: + if (!p) goto unexp; T( trace(T_PEER, "peer: received NOP packet"); ) break; case MISC_PING: + if (!p) goto unexp; buf_put(p_txstart(p, MSG_MISC | MISC_PONG), BCUR(&b), BLEFT(&b)); p_txend(p); break; case MISC_PONG: + if (!p) goto unexp; p_ponged(p, MISC_PONG, &b); break; case MISC_EPING: buf_init(&bb, buf_t, sizeof(buf_t)); - if (p_decrypt(p, ch, &b, &bb)) + if (p_decrypt(&p, &a, n, ch, &b, &bb)) return; if (BOK(&bb)) { buf_flip(&bb); @@ -298,7 +381,7 @@ static void p_read(int fd, unsigned mode, void *v) break; case MISC_EPONG: buf_init(&bb, buf_t, sizeof(buf_t)); - if (p_decrypt(p, ch, &b, &bb)) + if (p_decrypt(&p, &a, n, ch, &b, &bb)) return; if (BOK(&bb)) { buf_flip(&bb); @@ -308,13 +391,16 @@ static void p_read(int fd, unsigned mode, void *v) } break; default: - p->st.n_reject++; + if (p) p->st.n_reject++; a_warn("PEER", "?PEER", p, "bad-packet", "unknown-category" "0x%02x", ch, A_END); break; + unexp: + a_warn("PEER", "-", "unexpected-source", "?ADDR", &a, A_END); + break; } } @@ -737,31 +823,34 @@ peer *p_create(peerspec *spec) T( trace(T_PEER, "peer: creating new peer `%s'", spec->name); ) p->spec = *spec; p->spec.name = (/*unconst*/ char *)SYM_NAME(p->byname); + if (spec->tag) + p->spec.tag = xstrdup(spec->tag); p->ks = 0; p->pings = 0; p->ifname = 0; memset(&p->st, 0, sizeof(stats)); p->st.t_start = time(0); - if (!tops->open) + if (!(tops->flags & TUNF_PRIVOPEN)) fd = -1; - else if ((fd = tops->open(&p->ifname)) < 0) + else if ((fd = ps_tunfd(tops, &p->ifname)) < 0) goto tidy_2; if ((p->t = tops->create(p, fd, &p->ifname)) == 0) goto tidy_3; T( trace(T_TUNNEL, "peer: attached interface %s to peer `%s'", p->ifname, p_name(p)); ) p_setkatimer(p); - if (kx_init(&p->kx, p, &p->ks, p->spec.kxf)) + if (kx_init(&p->kx, p, &p->ks, p->spec.f & PSF_KXMASK)) goto tidy_4; a_notify("ADD", "?PEER", p, "%s", p->ifname, "?ADDR", &p->spec.sa, A_END); - if (!(p->spec.kxf & KXF_CORK)) { + if (!(p->spec.f & KXF_CORK)) { a_notify("KXSTART", "?PEER", p, A_END); /* Couldn't tell anyone before */ } + if (p->spec.f & PSF_MOBILE) nmobile++; return (p); tidy_4: @@ -773,6 +862,7 @@ tidy_3: if (fd >= 0) close(fd); tidy_2: am_remove(&byaddr, p->byaddr); + if (p->spec.tag) xfree(p->spec.tag); tidy_1: sym_remove(&byname, p->byname); tidy_0: @@ -787,7 +877,18 @@ tidy_0: * Returns: A pointer to the peer's name. */ -const char *p_name(peer *p) { return (p->spec.name); } +const char *p_name(peer *p) + { if (p) return (p->spec.name); else return ("-"); } + +/* --- @p_tag@ --- * + * + * Arguments: @peer *p@ = pointer to a peer block + * + * Returns: A pointer to the peer's public key tag. + */ + +const char *p_tag(peer *p) + { return (p->spec.tag ? p->spec.tag : p->spec.name); } /* --- @p_spec@ --- * * @@ -811,8 +912,10 @@ peer *p_findbyaddr(const addr *a) { peer_byaddr *pa; - if ((pa = am_find(&byaddr, a, 0, 0)) != 0) + if ((pa = am_find(&byaddr, a, 0, 0)) != 0) { + assert(pa->p); return (pa->p); + } return (0); } @@ -851,8 +954,12 @@ void p_destroy(peer *p) a_notify("KILL", "%s", p->spec.name, A_END); ksl_free(&p->ks); kx_free(&p->kx); + if (p->spec.f & PSF_MOBILE) + nmobile--; if (p->ifname) xfree(p->ifname); + if (p->spec.tag) + xfree(p->spec.tag); p->t->ops->destroy(p->t); if (p->spec.t_ka) sel_rmtimer(&p->tka);