ip/tinc: 4096 bit RSA, (passive) pmtu discovery, fix udpfd close() race, cleanup

- increase buffer size to support up to 4096 bit RSA keys
- handle PMTUDiscovery option and respond to pmtu probes
- handle port in Address option
- wlock(&netlk) before closing udpfd to sync with writers
- move default subnet handling out of gethost()
This commit is contained in:
cinap_lenrek 2019-03-10 19:07:58 +01:00
parent a0acae173e
commit d75f7d273b

View file

@ -177,6 +177,7 @@ void netrecalc(void);
int consend(Conn *c, char *fmt, ...);
#pragma varargck argpos consend 2
int sendudp(Host *h, int fd, uchar *p, int n);
void routepkt(Host *s, uchar *p, int n);
void needkey(Host *from);
void clearkey(Host *from);
@ -624,47 +625,50 @@ gethost(char *name, int new)
for(h = hosts; h != nil; h = h->next)
if(strcmp(h->name, name) == 0)
goto out;
n = -1;
snprint(buf, sizeof(buf), "hosts/%s", name);
if((fd = open(buf, OREAD)) < 0){
if(!new)
goto out;
buf[0] = 0;
} else {
if((fd = open(buf, OREAD)) >= 0){
n = read(fd, buf, sizeof(buf)-1);
close(fd);
if(n <= 0)
goto out;
buf[n] = 0;
}
if(n < 0){
if(!new)
goto out;
n = 0;
}
buf[n] = 0;
h = emalloc(sizeof(Host));
h->name = estrdup(name);
h->addr = estrdup(name);
h->port = 655;
h->pmtu = DefPMTU;
h->options = OptClampMss;
h->options = OptClampMss|OptPmtuDiscov;
h->udpfd = -1;
h->connected = 0;
h->rsapub = nil;
h->next = hosts;
hosts = h;
if((s = (char*)decodePEM(buf, "RSA PUBLIC KEY", &n, nil)) == nil)
goto out;
h->rsapub = asn1toRSApub((uchar*)s, n);
free(s);
if(h->rsapub == nil)
goto out;
if((s = (char*)decodePEM(buf, "RSA PUBLIC KEY", &n, nil)) != nil){
h->rsapub = asn1toRSApub((uchar*)s, n);
free(s);
}
for(s = buf; s != nil; s = e){
char *f[2];
if((e = strchr(s, '\n')) != nil)
*e++ = 0;
if((x = strchr(s, '=')) == nil)
if(*s == '#' || (x = strchr(s, '=')) == nil)
continue;
*x = ' ';
if((n = tokenize(s, f, nelem(f))) != 2)
if((n = tokenize(s, f, 2)) != 2)
continue;
if(cistrcmp(f[0], "Address") == 0){
if(tokenize(f[1], f, 2) > 1)
h->port = atoi(f[1]);
free(h->addr);
h->addr = estrdup(f[1]);
h->addr = estrdup(f[0]);
continue;
}
if(cistrcmp(f[0], "IndirectData") == 0){
@ -679,6 +683,10 @@ gethost(char *name, int new)
h->options &= ~(OptClampMss*(cistrcmp(f[1], "no") == 0));
continue;
}
if(cistrcmp(f[0], "PMTUDiscovery") == 0){
h->options &= ~(OptPmtuDiscov*(cistrcmp(f[1], "no") == 0));
continue;
}
if(cistrcmp(f[0], "PMTU") == 0){
h->pmtu = atoi(f[1]);
if(h->pmtu > MaxPacket)
@ -697,10 +705,6 @@ gethost(char *name, int new)
continue;
}
}
if(myhost == nil && h->snet == nil){
snprint(buf, sizeof(buf), "%I/%d", localip, isv4(localip) ? 32 : 128);
getsubnet(h, buf, 1);
}
parseip(h->ip, h->addr);
out:
qunlock(&hostslk);
@ -861,7 +865,7 @@ recvudp(Host *h, int fd)
}
memmove(cs, h->cin->cs, sizeof(cs));
(*h->cin->crypt)(buf, n, cs);
if((n -= buf[n-1]) < 4+EtherHdr){
if((n -= buf[n-1]) < 4){
unlock(h->cin);
return -1;
}
@ -885,18 +889,27 @@ recvudp(Host *h, int fd)
h->udpfd = fd;
unlock(h->cin);
/* pmtu probe */
if(n >= 4+14 && buf[4+12] == 0 && buf[4+13] == 0){
if(buf[4+0] == 0){
buf[4+0] = 1;
sendudp(h, fd, buf+4, n-4);
}
return 0;
}
routepkt(h, buf+4, n-4);
return 0;
}
int
sendudp(Host *h, uchar *p, int n)
sendudp(Host *h, int fd, uchar *p, int n)
{
uchar buf[4+MaxPacket+AESbsize+SHA2_256dlen];
AESstate cs[1];
uint seq;
int pad;
if(h->udpfd < 0 || n > MaxPacket || n > h->pmtu)
if(fd < 0 || n > MaxPacket)
return -1;
lock(h->cout);
if(h->cout->crypt == nil){
@ -919,7 +932,7 @@ sendudp(Host *h, uchar *p, int n)
hmac_sha2_256(buf, n, h->cout->key, sizeof(h->cout->key), buf+n, nil);
unlock(h->cout);
n += MAClen;
if(write(h->udpfd, buf, n) != n)
if(write(fd, buf, n) != n)
return -1;
if((seq & 0xFFFFF) == 0) needkey(h);
return 0;
@ -951,7 +964,7 @@ forward(Host *s, Host *d, uchar *p, int n)
if(d->from == nil)
return;
while(d != s && d != myhost){
if(sendudp(d, p, n) == 0)
if(n <= d->pmtu && sendudp(d, d->udpfd, p, n) == 0)
return;
if(sendtcp(d, p, n) == 0)
return;
@ -1085,7 +1098,7 @@ int
metaauth(Conn *c)
{
mpint *m, *h;
uchar b[256];
uchar b[512];
AuthRpc *rpc;
char *f[8];
int n, n1, n2, ms;
@ -1403,8 +1416,8 @@ tcpclient(int fd, int incoming)
procsetname("tcpclient %s %s %s %I!%d", myhost->name,
incoming ? "in" : "out", dir, c->ip, c->port);
if(metaauth(c) == 0){
procsetname("tcpclient %s %s %I!%d %s", myhost->name,
dir, c->ip, c->port, c->host->name);
procsetname("tcpclient %s %s %s %I!%d %s", myhost->name,
incoming ? "in" : "out", dir, c->ip, c->port, c->host->name);
metapeer(c);
}
netlock(c);
@ -1429,8 +1442,8 @@ udpclient(int fd, int incoming)
char dir[128];
Host *h;
if((h = findhost(ip, dir2ipport(fd2dir(fd, dir, sizeof(dir)), ip))) != nil
&& h != myhost){
h = findhost(ip, dir2ipport(fd2dir(fd, dir, sizeof(dir)), ip));
if(h != nil && h != myhost){
procsetname("udpclient %s %s %s %I!%d %s", myhost->name,
incoming ? "in": "out", dir, h->ip, h->port, h->name);
@ -1449,6 +1462,9 @@ udpclient(int fd, int incoming)
if(h->udpfd == fd)
h->udpfd = -1;
unlock(h->cin);
wlock(&netlk);
wunlock(&netlk);
}
close(fd);
}
@ -1656,6 +1672,11 @@ main(int argc, char *argv[])
sysfatal("no RSA public key for: %s", h->name);
}
if(myhost->snet == nil){
char snet[64];
snprint(snet, sizeof(snet), "%I/128", localip);
getsubnet(myhost, snet, 1);
}
if((t = lookupnet(localip)) == nil)
sysfatal("no subnet found for local ip %I", localip);
if(t->owner != myhost)