/*----- Global state ------------------------------------------------------*/
-sel_file udpsock[NADDRFAM];
+udpsocket udpsock[NADDRFAM];
/*----- Static variables --------------------------------------------------*/
})
buf_init(&b, buf_i, n);
buf_getbyte(&b);
- if (c_check(&b) || BLEFT(&b)) {
+ if (c_check(0, 0, &b) || BLEFT(&b)) {
a_warn("PEER", "-", "invalid-greeting", A_END);
return;
}
}
break;
case MSG_KEYEXCH:
- if (!p) goto unexp;
- p_rxupdstats(p, n);
- kx_message(&p->kx, ch & MSG_TYPEMASK, &b);
+ if (p) p_rxupdstats(p, n);
+ if (kx_message(p ? &p->kx : 0, &a, ch & MSG_TYPEMASK, &b)) goto unexp;
break;
case MSG_MISC:
switch (ch & MSG_TYPEMASK) {
p_ponged(p, MISC_EPONG, &bb);
}
break;
+ case MISC_BYE:
+ buf_init(&bb, buf_t, sizeof(buf_t));
+ if (p_decrypt(&p, &a, n, ch, &b, &bb)) return;
+ if (!(p->spec.f&PSF_EPHEM)) return;
+ if (BOK(&bb)) {
+ buf_flip(&bb);
+ if (BSZ(&bb)) return;
+ p_destroy(p, 0);
+ }
+ break;
}
break;
default:
return (&p->b);
}
+/* --- @p_txaddr@ --- *
+ *
+ * Arguments: @const addr *a@ = recipient address
+ * @const void *p@ = pointer to packet to send
+ * @size_t sz@ = length of packet
+ *
+ * Returns: Zero if successful, nonzero on error.
+ *
+ * Use: Sends a packet to an address which (possibly) isn't a current
+ * peer.
+ */
+
+int p_txaddr(const addr *a, const void *p, size_t sz)
+{
+ socklen_t sasz = addrsz(a);
+ int i;
+
+ if ((i = afix(a->sa.sa_family)) < 0) {
+ a_warn("PEER", "?ADDR", a, "disabled-address-family", A_END);
+ return (-1);
+ }
+ IF_TRACING(T_PEER, trace_block(T_PACKET, "peer: sending packet", p, sz); )
+ if (sendto(udpsock[i].sf.fd, p, sz, 0, &a->sa, sasz) < 0) {
+ a_warn("PEER", "?ADDR", a, "socket-write-error", "?ERRNO", A_END);
+ return (-1);
+ }
+ return (0);
+}
+
/* --- @p_txend@ --- *
*
* Arguments: @peer *p@ = pointer to peer block
}
IF_TRACING(T_PEER, trace_block(T_PACKET, "peer: sending packet",
BBASE(&p->b), BLEN(&p->b)); )
- if (sendto(udpsock[p->afix].fd, BBASE(&p->b), BLEN(&p->b),
+ if (sendto(udpsock[p->afix].sf.fd, BBASE(&p->b), BLEN(&p->b),
0, &p->spec.sa.sa, sasz) < 0) {
a_warn("PEER", "?PEER", p, "socket-write-error", "?ERRNO", A_END);
return (0);
{
int fd;
int len = PKBUFSZ;
+ int yes = 1;
int i;
struct addrinfo *ai;
unsigned port, lastport = 0;
addr a;
socklen_t sz;
- for (i = 0; i < NADDRFAM; i++) udpsock[i].fd = -1;
+ for (i = 0; i < NADDRFAM; i++) udpsock[i].sf.fd = -1;
for (ai = ailist; ai; ai = ai->ai_next) {
if ((i = afix(ai->ai_family)) < 0) continue;
- if (udpsock[i].fd != -1) continue;
+ if (udpsock[i].sf.fd != -1) continue;
/* --- Note on socket buffer sizes --- *
*
if ((fd = socket(ai->ai_family, SOCK_DGRAM, 0)) < 0)
die(EXIT_FAILURE, "socket creation failed: %s", strerror(errno));
+ if (i == AFIX_INET6 &&
+ setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes))) {
+ die(EXIT_FAILURE, "failed to set IPv6-only state: %s",
+ strerror(errno));
+ }
assert(ai->ai_addrlen <= sizeof(a));
memcpy(&a, ai->ai_addr, ai->ai_addrlen);
if ((port = getport(&a)) == 0 && lastport) setport(&a, lastport);
strerror(errno));
}
fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
- sel_initfile(&sel, &udpsock[i], fd, SEL_READ, p_read, 0);
- sel_addfile(&udpsock[i]);
+ sel_initfile(&sel, &udpsock[i].sf, fd, SEL_READ, p_read, 0);
+ sel_addfile(&udpsock[i].sf);
T( trace(T_PEER, "peer: created %s socket", aftab[i].name); )
- if (!port) {
+ if (port)
+ udpsock[i].port = port;
+ else {
sz = sizeof(a);
if (getsockname(fd, &a.sa, &sz)) {
die(EXIT_FAILURE, "failed to read local socket address: %s",
strerror(errno));
}
- lastport = getport(&a);
+ udpsock[i].port = lastport = getport(&a);
}
}
am_create(&byaddr);
}
-/* --- @p_port@ --- *
- *
- * Arguments: @int i@ = address family index to retrieve
- *
- * Returns: Port number used for socket.
- */
-
-unsigned p_port(int i)
-{
- addr a;
- socklen_t sz = sizeof(addr);
-
- if (getsockname(udpsock[i].fd, &a.sa, &sz))
- die(EXIT_FAILURE, "couldn't read port number: %s", strerror(errno));
- return (getport(&a));
-}
-
/* --- @p_keepalive@ --- *
*
* Arguments: @struct timeval *now@ = the current time
p->spec.name = (/*unconst*/ char *)SYM_NAME(p->byname);
if (spec->tag) p->spec.tag = xstrdup(spec->tag);
if (spec->privtag) p->spec.privtag = xstrdup(spec->privtag);
+ if (spec->knock) p->spec.knock = xstrdup(spec->knock);
p->ks = 0;
p->pings = 0;
p->ifname = 0;
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.f & PSF_KXMASK))
+ if (kx_setup(&p->kx, p, &p->ks, p->spec.f & PSF_KXMASK))
goto tidy_4;
a_notify("ADD",
"?PEER", p,
/* --- @p_destroy@ --- *
*
* Arguments: @peer *p@ = pointer to a peer
+ * @int bye@ = say goodbye to the peer?
*
* Returns: ---
*
* Use: Destroys a peer.
*/
-void p_destroy(peer *p)
+void p_destroy(peer *p, int bye)
{
ping *pg, *ppg;
+ buf *b, bb;
T( trace(T_PEER, "peer: destroying peer `%s'", p->spec.name); )
+
+ if (bye && (p->spec.f&PSF_EPHEM)) {
+ b = p_txstart(p, MSG_MISC | MISC_BYE);
+ buf_init(&bb, buf_t, sizeof(buf_t));
+ assert(BOK(&bb)); buf_flip(&bb);
+ p_encrypt(p, MSG_MISC | MISC_BYE, &bb, b);
+ p_txend(p);
+ }
+
a_notify("KILL", "%s", p->spec.name, A_END);
ksl_free(&p->ks);
kx_free(&p->kx);
if (p->ifname) xfree(p->ifname);
if (p->spec.tag) xfree(p->spec.tag);
if (p->spec.privtag) xfree(p->spec.privtag);
+ if (p->spec.knock) xfree(p->spec.knock);
p->t->ops->destroy(p->t);
if (p->spec.t_ka) sel_rmtimer(&p->tka);
for (pg = p->pings; pg; pg = ppg) {