From: Mark Wooding Date: Mon, 7 May 2012 11:29:33 +0000 (+0100) Subject: server/peer.c: Handle mobile peers switching addresses. X-Git-Tag: 1.0.0pre11~8 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/tripe/commitdiff_plain/c71be7580b0f818492f5ceecb1a2b6ccb87c0712 server/peer.c: Handle mobile peers switching addresses. Previously, we would track a mobile peer if it changed its address to one that wasn't currently in use by any peer; but it seems that some benighted NAT boxes will actually mix up the addresses assigned to active peers, so we must spot the decryption errors and try to match them up again. --- diff --git a/server/peer.c b/server/peer.c index a62b20a8..02fceed5 100644 --- a/server/peer.c +++ b/server/peer.c @@ -188,45 +188,71 @@ static int p_encrypt(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) { - peer *p; - peer_byaddr *pa; + peer *p, *q; + peer_byaddr *pa, *qa; int err = KSERR_DECRYPT; unsigned f; - if (*pp) { - p = *pp; + /* --- If we have a match on the source address then try that first --- */ + + q = *pp; + if (q) { T( trace(T_PEER, "peer: decrypting packet from known peer `%s'", - p_name(p)); ) - err = ksl_decrypt(&p->ks, ty, bin, bout); - } else { + p_name(q)); ) + if ((err = ksl_decrypt(&q->ks, ty, bin, bout)) != KSERR_DECRYPT || + !(q->spec.f & PSF_MOBILE) || nmobile == 1) { + p = q; + goto match; + } + T( trace(T_PEER, "peer: failed to decrypt: try other mobile peers...", + p_name(q)); ) + } else if (nmobile) + T( trace(T_PEER, "peer: unknown source: trying mobile peers..."); ) + else { p = 0; - if (nmobile) { - T( trace(T_PEER, "peer: unknown source: trying mobile peers..."); ) - FOREACH_PEER(q, { - if (!(q->spec.f & PSF_MOBILE)) continue; - 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; + goto searched; + } + + /* --- See whether any mobile peer is interested --- */ + + FOREACH_PEER(pp, { + if (pp == q || !(pp->spec.f & PSF_MOBILE)) continue; + if ((err = ksl_decrypt(&pp->ks, ty, bin, bout)) == KSERR_DECRYPT) { + T( trace(T_PEER, "peer: peer `%s' failed to decrypt", + p_name(pp)); ) + continue; + } else { + p = pp; + IF_TRACING(T_PEER, { + if (!err) + trace(T_PEER, "peer: peer `%s' reports success", p_name(pp)); + else { + trace(T_PEER, "peer: peer `%s' reports decryption error %d", + p_name(pp), err); } - }); - } - if (!p) { - a_warn("PEER", "-", "unexpected-source", "?ADDR", a, A_END); - return (-1); + }) + break; } - if (!err) { + }); + + /* --- We've searched the mobile peers --- */ + +searched: + if (!p) { + a_warn("PEER", "-", "unexpected-source", "?ADDR", a, A_END); + return (-1); + } + + /* --- We found one that accepted, so update the peer's address --- * + * + * If we had an initial guess of which peer this packet came from -- i.e., + * @q@ is not null -- then swap the addresses over. This doesn't leave the + * evicted peer in an especially good state, but it ought to get sorted out + * soon enough. + */ + + if (!err) { + if (!q) { T( trace(T_PEER, "peer: updating address for `%s'", p_name(p)); ) pa = am_find(&byaddr, a, sizeof(peer_byaddr), &f); assert(!f); am_remove(&byaddr, p->byaddr); @@ -234,8 +260,18 @@ static int p_decrypt(peer **pp, addr *a, size_t n, pa->p = p; p->spec.sa = *a; a_notify("NEWADDR", "?PEER", p, "?ADDR", a, A_END); + } else { + T( trace(T_PEER, "peer: swapping addresses for `%s' and `%s'", + p_name(p), p_name(q)); ) + pa = p->byaddr; qa = q->byaddr; + pa->p = q; q->byaddr = pa; q->spec.sa = p->spec.sa; + qa->p = p; p->byaddr = qa; p->spec.sa = *a; + a_notify("NEWADDR", "?PEER", p, "?ADDR", a, A_END); + a_notify("NEWADDR", "?PEER", q, "?ADDR", &q->spec.sa, A_END); } } + +match: p_rxupdstats(p, n); if (err) { if (p) p->st.n_reject++;