shithub: riscv

Download patch

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);