Blob Blame History Raw
From: Evgeniy Manachkin <sfstudio@mail.ru>
Date: Mon, 30 Mar 2015 20:05:52 +0600
Subject: dhcpv6: ignore advertise messages with none of requested data and
 missed status codes.

with RENEW fix. Thanks TheMiron.

[ Roger Shimizu's comment ]
The patch was originally from:
- https://sourceforge.net/p/wide-dhcpv6/bugs/34/
- https://gitorious.org/wive-ng-mt/wive-ng-mt/commit/844e8a1a0d9026ba6fef8b224583e97890ca522e
- https://gitorious.org/wive-ng-mt/wive-ng-mt/commit/3f93dfec7a13c086f891b2ab9a974de2331d9b7d

This resolved upstream issue #34:
- https://sourceforge.net/p/wide-dhcpv6/bugs/34

Closes: #765453
---
 dhcp6c.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 55 insertions(+), 12 deletions(-)

diff --git a/dhcp6c.c b/dhcp6c.c
index 4e1356b..849835e 100644
--- a/dhcp6c.c
+++ b/dhcp6c.c
@@ -1484,10 +1484,10 @@ client6_recv()
 
 	switch(dh6->dh6_msgtype) {
 	case DH6_ADVERTISE:
-		(void)client6_recvadvert(ifp, dh6, len, &optinfo);
+		client6_recvadvert(ifp, dh6, len, &optinfo);
 		break;
 	case DH6_REPLY:
-		(void)client6_recvreply(ifp, dh6, len, &optinfo);
+		client6_recvreply(ifp, dh6, len, &optinfo);
 		break;
 	default:
 		debug_printf(LOG_INFO, FNAME, "received an unexpected message (%s) "
@@ -1511,6 +1511,7 @@ client6_recvadvert(ifp, dh6, len, optinfo)
 	struct dhcp6_event *ev;
 	struct dhcp6_eventdata *evd;
 	struct authparam *authparam = NULL, authparam0;
+	int have_ia = -1;
 
 	/* find the corresponding event based on the received xid */
 	ev = find_event_withid(ifp, ntohl(dh6->dh6_xid) & DH6_XIDMASK);
@@ -1549,38 +1550,80 @@ client6_recvadvert(ifp, dh6, len, optinfo)
 	 * includes a Status Code option containing the value NoPrefixAvail
 	 * [RFC3633 Section 11.1].
 	 * Likewise, the client MUST ignore any Advertise message that includes
-	 * a Status Code option containing the value NoAddrsAvail. 
+	 * a Status Code option containing the value NoAddrsAvail.
 	 * [RFC3315 Section 17.1.3].
 	 * We only apply this when we are going to request an address or
 	 * a prefix.
 	 */
-	for (evd = TAILQ_FIRST(&ev->data_list); evd;
-	    evd = TAILQ_NEXT(evd, link)) {
+	for (evd = TAILQ_FIRST(&ev->data_list); evd; evd = TAILQ_NEXT(evd, link)) {
+		struct dhcp6_listval *lv, *slv;
 		u_int16_t stcode;
 		char *stcodestr;
 
 		switch (evd->type) {
-		case DHCP6_EVDATA_IAPD:
+		    case DHCP6_EVDATA_IAPD:
 			stcode = DH6OPT_STCODE_NOPREFIXAVAIL;
 			stcodestr = "NoPrefixAvail";
 			break;
-		case DHCP6_EVDATA_IANA:
+		    case DHCP6_EVDATA_IANA:
 			stcode = DH6OPT_STCODE_NOADDRSAVAIL;
 			stcodestr = "NoAddrsAvail";
 			break;
-		default:
+		    default:
 			continue;
 		}
+
 		if (dhcp6_find_listval(&optinfo->stcode_list,
 		    DHCP6_LISTVAL_STCODE, &stcode, 0)) {
-			debug_printf(LOG_INFO, FNAME,
-			    "advertise contains %s status", stcodestr);
+			debug_printf(LOG_INFO, FNAME, "advertise contains %s status", stcodestr);
 			return (-1);
 		}
+
+		if (have_ia > 0 || TAILQ_EMPTY((struct dhcp6_list *)evd->data))
+		    continue;
+
+		have_ia = 0;
+		/* parse list of IA_PD */
+		if (evd->type == DHCP6_EVDATA_IAPD) {
+		    TAILQ_FOREACH(lv, (struct dhcp6_list *)evd->data, link) {
+			slv = dhcp6_find_listval(&optinfo->iapd_list, DHCP6_LISTVAL_IAPD, &lv->val_ia, 0);
+			if (slv == NULL)
+				continue;
+			TAILQ_FOREACH(slv, &slv->sublist, link) {
+			    if (slv->type == DHCP6_LISTVAL_PREFIX6) {
+				have_ia = 1;
+				break;
+			    }
+			}
+		    }
+		}
+		/* parse list of IA_NA */
+		if (evd->type == DHCP6_EVDATA_IANA) {
+		    TAILQ_FOREACH(lv, (struct dhcp6_list *)evd->data, link) {
+			slv = dhcp6_find_listval(&optinfo->iana_list, DHCP6_LISTVAL_IANA, &lv->val_ia, 0);
+			if (slv == NULL)
+				continue;
+			TAILQ_FOREACH(slv, &slv->sublist, link) {
+			    if (slv->type == DHCP6_LISTVAL_STATEFULADDR6) {
+				have_ia = 1;
+				break;
+			    }
+			}
+		    }
+		}
+	}
+
+	/*
+	 * Ignore message with none of requested addresses and/or
+	 * a prefixes as if NoAddrsAvail/NoPrefixAvail Status Code
+	 * was included.
+	 */
+	if (have_ia == 0) {
+		debug_printf(LOG_INFO, FNAME, "advertise contains no address/prefix");
+		return (-1);
 	}
 
-	if (ev->state != DHCP6S_SOLICIT ||
-	    (ifp->send_flags & DHCIFF_RAPID_COMMIT) || infreq_mode) {
+	if (ev->state != DHCP6S_SOLICIT || (ifp->send_flags & DHCIFF_RAPID_COMMIT) || infreq_mode) {
 		/*
 		 * We expected a reply message, but do actually receive an
 		 * Advertise message.  The server should be configured not to