ref: 82c08a8a4469c4b4ba515d74addc7aac747a17d9
parent: 4e938841f02de646d4e2e428453cbeede4051a57
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sat Aug 25 11:10:26 EDT 2018
ip/dhcpd: handle multiple networks on the same interface, dont get confused by v6 interface address
--- a/sys/src/cmd/ip/dhcpd/dat.h
+++ b/sys/src/cmd/ip/dhcpd/dat.h
@@ -28,14 +28,18 @@
struct Info
{
int indb; /* true if found in database */
+
+ Ipifc *ifc; /* ifc when directly connected */
+
+ uchar ipaddr[NDB_IPlen]; /* ip address of system */
+ uchar ipmask[NDB_IPlen]; /* ip network mask */
+ uchar ipnet[NDB_IPlen]; /* ip network address (ipaddr & ipmask) */
+
char domain[Maxstr]; /* system domain name */
char bootf[Maxstr]; /* boot file */
char bootf2[Maxstr]; /* alternative boot file */
uchar tftp[NDB_IPlen]; /* ip addr of tftp server */
uchar tftp2[NDB_IPlen]; /* ip addr of alternate server */
- uchar ipaddr[NDB_IPlen]; /* ip address of system */
- uchar ipmask[NDB_IPlen]; /* ip network mask */
- uchar ipnet[NDB_IPlen]; /* ip network address (ipaddr & ipmask) */
uchar etheraddr[6]; /* ethernet address */
uchar gwip[NDB_IPlen]; /* gateway ip address */
uchar fsip[NDB_IPlen]; /* file system ip address */
@@ -68,8 +72,9 @@
extern int lookup(Bootp*, Info*, Info*);
extern int lookupip(uchar*, Info*, int);
extern void lookupname(char*, int, Ndbtuple*);
-extern Iplifc* findlifc(uchar*);
-extern int forme(uchar*);
+extern Ipifc* findifc(uchar*);
+extern Iplifc* findlifc(uchar*, Ipifc*);
+extern void localip(uchar*, uchar*, Ipifc*);
extern int lookupserver(char*, uchar**, int, Ndbtuple *t);
extern Ndbtuple* lookupinfo(uchar *ipaddr, char **attr, int n);
@@ -82,4 +87,4 @@
extern Ipifc *ipifcs;
extern long now;
extern char *ndbfile;
-
+extern uchar zeros[];
--- a/sys/src/cmd/ip/dhcpd/db.c
+++ b/sys/src/cmd/ip/dhcpd/db.c
@@ -209,6 +209,19 @@
{
uchar x[IPaddrlen];
+ /* directly connected, check local networks */
+ if(iip->ifc != nil){
+ Iplifc *lifc;
+
+ for(lifc = iip->ifc->lifc; lifc != nil; lifc = lifc->next){
+ maskip(lifc->mask, ip, x);
+ if(ipcmp(x, lifc->net) == 0)
+ return 1;
+ }
+ return 0;
+ }
+
+ /* relay agent, check upstream network */
maskip(iip->ipmask, ip, x);
return ipcmp(x, iip->ipnet) == 0;
}
--- a/sys/src/cmd/ip/dhcpd/dhcpd.c
+++ b/sys/src/cmd/ip/dhcpd/dhcpd.c
@@ -19,6 +19,7 @@
int fd; /* for reply */
Bootp *bp;
Udphdr *up;
+ Ipifc *ifc;
uchar *e; /* end of received message */
uchar *p; /* options pointer */
uchar *max; /* max end of reply */
@@ -164,7 +165,7 @@
void addropt(Req*, int, uchar*);
void addrsopt(Req*, int, uchar**, int);
-void arpenter(uchar*, uchar*);
+void arpenter(uchar*, uchar*, uchar*);
void bootp(Req*);
void byteopt(Req*, int, uchar);
void dhcp(Req*);
@@ -188,7 +189,7 @@
char* readsysname(void);
void remrequested(Req*, int);
void sendack(Req*, uchar*, int, int);
-void sendnak(Req*, char*);
+void sendnak(Req*, uchar*, char*);
void sendoffer(Req*, uchar*, int);
void stringopt(Req*, int, char*);
void termopt(Req*);
@@ -324,7 +325,6 @@
void
proto(Req *rp, int n)
{
- uchar relip[IPaddrlen];
char buf[64];
now = time(0);
@@ -332,8 +332,6 @@
rp->e = rp->buf + n;
rp->bp = (Bootp*)rp->buf;
rp->up = (Udphdr*)rp->buf;
- if (ipcmp(rp->up->laddr, IPv4bcast) == 0)
- ipmove(rp->up->laddr, rp->up->ifcaddr);
rp->max = rp->buf + Udphdrsize + MINSUPPORTED - IPUDPHDRSIZE;
rp->p = rp->bp->optdata;
v4tov6(rp->giaddr, rp->bp->giaddr);
@@ -342,22 +340,15 @@
if(pptponly && rp->bp->htype != 0)
return;
- ipifcs = readipifc(net, ipifcs, -1);
- if(validip(rp->giaddr))
- ipmove(relip, rp->giaddr);
- else if(validip(rp->up->raddr))
- ipmove(relip, rp->up->raddr);
- else
- ipmove(relip, rp->up->laddr);
- if(rp->e < (uchar*)rp->bp->sname){
- warning(0, "packet too short");
- return;
- }
if(rp->bp->op != Bootrequest){
warning(0, "not bootrequest");
return;
}
+ if(rp->e < (uchar*)rp->bp->sname){
+ warning(0, "packet too short");
+ return;
+ }
if(rp->e >= rp->bp->optdata){
if(memcmp(rp->bp->optmagic, plan9opt, sizeof(rp->bp->optmagic)) == 0)
rp->p9request = 1;
@@ -385,12 +376,26 @@
rp->id = tohex(buf, rp->bp->chaddr, rp->bp->hlen);
}
- /* info about gateway */
- if(lookupip(relip, &rp->gii, 1) < 0){
- warning(0, "lookupip failed");
+ ipifcs = readipifc(net, ipifcs, -1);
+ rp->ifc = findifc(rp->up->ifcaddr);
+ if(rp->ifc == nil){
+ warning(0, "no interface");
return;
}
+ if(validip(rp->giaddr)){
+ /* info about gateway */
+ if(lookupip(rp->giaddr, &rp->gii, 1) < 0){
+ warning(0, "lookupip failed");
+ return;
+ }
+ rp->gii.ifc = nil;
+ } else {
+ /* no gateway, directly connected */
+ memset(&rp->gii, 0, sizeof(rp->gii));
+ rp->gii.ifc = rp->ifc;
+ }
+
/* info about target system */
if(lookup(rp->bp, &rp->ii, &rp->gii) == 0)
if(rp->ii.indb && rp->ii.dhcpgroup[0] == 0)
@@ -498,13 +503,13 @@
/* check for hard assignment */
if(rp->staticbinding){
- if(forme(rp->server))
+ if(findifc(rp->server) != rp->ifc) {
+ warning(0, "!Request(%s via %I): for server %I not me",
+ rp->id, rp->gii.ipaddr, rp->server);
+ } else
sendack(rp, rp->ii.ipaddr,
(staticlease > minlease? staticlease:
minlease), 1);
- else
- warning(0, "!Request(%s via %I): for server %I not me",
- rp->id, rp->gii.ipaddr, rp->server);
return;
}
@@ -514,13 +519,13 @@
if(b == nil){
warning(0, "!Request(%s via %I): no offer",
rp->id, rp->gii.ipaddr);
- if(forme(rp->server))
- sendnak(rp, "no offer for you");
+ if(findifc(rp->server) == rp->ifc)
+ sendnak(rp, rp->server, "no offer for you");
return;
}
/* if not for me, retract offer */
- if(!forme(rp->server)){
+ if(findifc(rp->server) != rp->ifc){
b->expoffer = 0;
warning(0, "!Request(%s via %I): for server %I not me",
rp->id, rp->gii.ipaddr, rp->server);
@@ -534,13 +539,13 @@
if(validip(rp->ip) && ipcmp(rp->ip, b->ip) != 0){
warning(0, "!Request(%s via %I): requests %I, not %I",
rp->id, rp->gii.ipaddr, rp->ip, b->ip);
- sendnak(rp, "bad ip address option");
+ sendnak(rp, rp->ip, "bad ip address option");
return;
}
if(commitbinding(b) < 0){
warning(0, "!Request(%s via %I): can't commit %I",
rp->id, rp->gii.ipaddr, b->ip);
- sendnak(rp, "can't commit binding");
+ sendnak(rp, b->ip, "can't commit binding");
return;
}
sendack(rp, b->ip, b->offer, 1);
@@ -556,10 +561,10 @@
if(ipcmp(rp->ip, rp->ii.ipaddr) != 0){
warning(0, "!Request(%s via %I): %I not valid for %E",
rp->id, rp->gii.ipaddr, rp->ip, rp->bp->chaddr);
- sendnak(rp, "not valid");
- }
- sendack(rp, rp->ii.ipaddr, (staticlease > minlease?
- staticlease: minlease), 1);
+ sendnak(rp, rp->ip, "not valid");
+ } else
+ sendack(rp, rp->ip, (staticlease > minlease?
+ staticlease: minlease), 1);
return;
}
@@ -567,7 +572,6 @@
if(!samenet(rp->ip, &rp->gii)){
warning(0, "!Request(%s via %I): bad forward of %I",
rp->id, rp->gii.ipaddr, rp->ip);
- sendnak(rp, "wrong network");
return;
}
b = iptobinding(rp->ip, 0);
@@ -579,7 +583,7 @@
if(ipcmp(rp->ip, b->ip) != 0 || now > b->lease){
warning(0, "!Request(%s via %I): %I not valid",
rp->id, rp->gii.ipaddr, rp->ip);
- sendnak(rp, "not valid");
+ sendnak(rp, rp->ip, "not valid");
return;
}
b->offer = b->lease - now;
@@ -599,10 +603,10 @@
if(ipcmp(rp->ciaddr, rp->ii.ipaddr) != 0){
warning(0, "!Request(%s via %I): %I not valid",
rp->id, rp->gii.ipaddr, rp->ciaddr);
- sendnak(rp, "not valid");
- }
- sendack(rp, rp->ii.ipaddr, (staticlease > minlease?
- staticlease: minlease), 1);
+ sendnak(rp, rp->ciaddr, "not valid");
+ } else
+ sendack(rp, rp->ciaddr, (staticlease > minlease?
+ staticlease: minlease), 1);
return;
}
@@ -610,19 +614,18 @@
if(!samenet(rp->ciaddr, &rp->gii)){
warning(0, "!Request(%s via %I): bad forward of %I",
rp->id, rp->gii.ipaddr, rp->ip);
- sendnak(rp, "wrong network");
return;
}
b = iptobinding(rp->ciaddr, 0);
if(b == nil){
warning(0, "!Request(%s via %I): no binding for %I",
- rp->id, rp->gii.ipaddr, rp->ciaddr);
+ rp->id, rp->ciaddr, rp->ciaddr);
return;
}
if(ipcmp(rp->ciaddr, b->ip) != 0){
- warning(0, "!Request(%I via %s): %I not valid",
+ warning(0, "!Request(%s via %I): %I not valid",
rp->id, rp->gii.ipaddr, rp->ciaddr);
- sendnak(rp, "invalid ip address");
+ sendnak(rp, rp->ciaddr, "invalid ip address");
return;
}
mkoffer(b, rp->id, rp->leasetime);
@@ -629,7 +632,7 @@
if(commitbinding(b) < 0){
warning(0, "!Request(%s via %I): can't commit %I",
rp->id, rp->gii.ipaddr, b->ip);
- sendnak(rp, "can't commit binding");
+ sendnak(rp, b->ip, "can't commit binding");
return;
}
sendack(rp, b->ip, b->offer, 1);
@@ -730,9 +733,8 @@
bp = rp->bp;
up = rp->up;
- /*
- * set destination
- */
+ localip(up->laddr, ip, rp->ifc);
+
flags = nhgets(bp->flags);
if(validip(rp->giaddr)){
ipmove(up->raddr, rp->giaddr);
@@ -743,7 +745,7 @@
} else {
ipmove(up->raddr, ip);
if(bp->htype == 1)
- arpenter(up->raddr, bp->chaddr);
+ arpenter(up->raddr, bp->chaddr, up->ifcaddr);
hnputs(up->rport, 68);
}
@@ -790,9 +792,8 @@
bp = rp->bp;
up = rp->up;
- /*
- * set destination
- */
+ localip(up->laddr, ip, rp->ifc);
+
flags = nhgets(bp->flags);
if(validip(rp->giaddr)){
ipmove(up->raddr, rp->giaddr);
@@ -803,7 +804,7 @@
} else {
ipmove(up->raddr, ip);
if(bp->htype == 1)
- arpenter(up->raddr, bp->chaddr);
+ arpenter(up->raddr, bp->chaddr, up->ifcaddr);
hnputs(up->rport, 68);
}
@@ -841,7 +842,7 @@
}
void
-sendnak(Req *rp, char *msg)
+sendnak(Req *rp, uchar *ip, char *msg)
{
int n;
Bootp *bp;
@@ -850,6 +851,8 @@
bp = rp->bp;
up = rp->up;
+ localip(up->laddr, ip, rp->ifc);
+
/*
* set destination (always broadcast)
*/
@@ -900,7 +903,6 @@
Bootp *bp;
Udphdr *up;
ushort flags;
- Iplifc *lifc;
Info *iip;
warning(0, "bootp %s %I->%I from %s via %I, file %s",
@@ -960,10 +962,8 @@
if(rp->p9request){
warning(0, "p9bootp: %I", iip->ipaddr);
memmove(bp->optmagic, plan9opt, 4);
- if(iip->gwip == 0)
- v4tov6(iip->gwip, bp->giaddr);
- rp->p += sprint((char*)rp->p, "%V %I %I %I", iip->ipmask+IPv4off, iip->fsip,
- iip->auip, iip->gwip);
+ rp->p += sprint((char*)rp->p, "%V %I %I %I",
+ iip->ipmask+IPv4off, iip->fsip, iip->auip, iip->gwip);
sprint(optbuf, "%s", (char*)(bp->optmagic));
} else if(rp->genrequest){
warning(0, "genericbootp: %I", iip->ipaddr);
@@ -979,9 +979,8 @@
rp->p += 128-4;
}
- /*
- * set destination
- */
+ localip(up->laddr, iip->ipaddr, iip->ifc);
+
flags = nhgets(bp->flags);
if(validip(rp->giaddr)){
ipmove(up->raddr, rp->giaddr);
@@ -992,18 +991,11 @@
} else {
v4tov6(up->raddr, bp->yiaddr);
if(bp->htype == 1)
- arpenter(up->raddr, bp->chaddr);
+ arpenter(up->raddr, bp->chaddr, up->ifcaddr);
hnputs(up->rport, 68);
}
/*
- * select best local address if destination is directly connected
- */
- lifc = findlifc(up->raddr);
- if(lifc)
- ipmove(up->laddr, lifc->ip);
-
- /*
* our identity
*/
strncpy(bp->sname, mysysname, sizeof(bp->sname));
@@ -1136,14 +1128,24 @@
char *p;
char *attr[100], **a;
Ndbtuple *t;
+ Iplifc *lifc;
for(i=0; i<nelem(addrs); i++)
addrs[i] = &x[i*IPaddrlen];
/* always supply these */
- maskopt(rp, OBmask, rp->gii.ipmask);
- if(validip(rp->gii.gwip)){
+ if(validip(rp->ii.ipmask))
+ maskopt(rp, OBmask, rp->ii.ipmask);
+ else if(validip(rp->gii.ipmask))
+ maskopt(rp, OBmask, rp->gii.ipmask);
+ else if((lifc = findlifc(ip, rp->ifc)) != nil)
+ maskopt(rp, OBmask, lifc->mask);
+
+ if(validip(rp->ii.gwip)){
remrequested(rp, OBrouter);
+ addropt(rp, OBrouter, rp->ii.gwip);
+ } else if(validip(rp->gii.gwip)){
+ remrequested(rp, OBrouter);
addropt(rp, OBrouter, rp->gii.gwip);
} else if(validip(rp->giaddr)){
remrequested(rp, OBrouter);
@@ -1581,7 +1583,7 @@
}
void
-arpenter(uchar *ip, uchar *ether)
+arpenter(uchar *ip, uchar *ether, uchar *ifcaddr)
{
int f;
char buf[256];
@@ -1592,7 +1594,7 @@
syslog(debug, blog, "open %s: %r", buf);
return;
}
- fprint(f, "add ether %I %E", ip, ether);
+ fprint(f, "add ether %I %E %I", ip, ether, ifcaddr);
close(f);
}
@@ -1650,7 +1652,7 @@
void
logdhcpout(Req *rp, char *type)
{
- syslog(0, blog, "%s(%I-%I)id(%s)ci(%V)gi(%V)yi(%V)si(%V) %s",
+ syslog(0, blog, "%s(%I->%I)id(%s)ci(%V)gi(%V)yi(%V)si(%V) %s",
type, rp->up->laddr, rp->up->raddr, rp->id,
rp->bp->ciaddr, rp->bp->giaddr, rp->bp->yiaddr, rp->bp->siaddr, optbuf);
}
--- a/sys/src/cmd/ip/dhcpd/ndb.c
+++ b/sys/src/cmd/ip/dhcpd/ndb.c
@@ -33,39 +33,49 @@
return db;
}
-Iplifc*
-findlifc(uchar *ip)
+Ipifc*
+findifc(uchar *ip)
{
- uchar x[IPaddrlen];
Ipifc *ifc;
Iplifc *lifc;
for(ifc = ipifcs; ifc != nil; ifc = ifc->next){
- for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
- if(lifc->net[0] == 0)
- continue;
- maskip(ip, lifc->mask, x);
- if(ipcmp(x, lifc->net) == 0)
- return lifc;
- }
+ for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next)
+ if(ipcmp(ip, lifc->ip) == 0)
+ return ifc;
}
return nil;
}
-int
-forme(uchar *ip)
+Iplifc*
+findlifc(uchar *ip, Ipifc *ifc)
{
- Ipifc *ifc;
+ uchar x[IPaddrlen];
Iplifc *lifc;
- for(ifc = ipifcs; ifc != nil; ifc = ifc->next){
- for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next)
- if(ipcmp(ip, lifc->ip) == 0)
- return 1;
+ if(ifc == nil)
+ return nil;
+
+ for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
+ maskip(ip, lifc->mask, x);
+ if(ipcmp(x, lifc->net) == 0)
+ return lifc;
}
- return 0;
+ return nil;
}
+void
+localip(uchar *laddr, uchar *raddr, Ipifc *ifc)
+{
+ Iplifc *lifc;
+
+ if((lifc = findlifc(raddr, ifc)) != nil)
+ ipmove(laddr, lifc->ip);
+ else if(ipcmp(laddr, IPv4bcast) == 0)
+ ipmove(laddr, IPnoaddr);
+}
+
+
uchar noetheraddr[6];
static void
@@ -190,8 +200,6 @@
return 0;
}
-static uchar zeroes[6];
-
/*
* lookup info about a client in the database. Find an address on the
* same net as riip.
@@ -215,21 +223,21 @@
/* client knows its address? */
v4tov6(ciaddr, bp->ciaddr);
if(validip(ciaddr)){
+ if(!samenet(ciaddr, riip)){
+ warning(0, "%I not on %I", ciaddr, riip->ipnet);
+ return -1;
+ }
if(lookupip(ciaddr, iip, 0) < 0) {
if (debug)
warning(0, "don't know %I", ciaddr);
return -1; /* don't know anything about it */
}
- if(!samenet(riip->ipaddr, iip)){
- warning(0, "%I not on %I", ciaddr, riip->ipnet);
- return -1;
- }
/*
* see if this is a masquerade, i.e., if the ether
* address doesn't match what we expected it to be.
*/
- if(memcmp(iip->etheraddr, zeroes, 6) != 0)
+ if(memcmp(iip->etheraddr, zeros, 6) != 0)
if(memcmp(bp->chaddr, iip->etheraddr, 6) != 0)
warning(0, "ciaddr %I rcvd from %E instead of %E",
ciaddr, bp->chaddr, iip->etheraddr);
@@ -262,14 +270,12 @@
continue;
if(parseip(ciaddr, nt->val) == -1)
continue;
- if(!validip(ciaddr))
+ if(!validip(ciaddr) || !samenet(ciaddr, riip))
continue;
if(lookupip(ciaddr, iip, 0) < 0)
continue;
- if(samenet(riip->ipaddr, iip)){
- ndbfree(t);
- return 0;
- }
+ ndbfree(t);
+ return 0;
}
ndbfree(t);
t = ndbsnext(&s, hwattr, hwval);