@@ -954,7 +954,8 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
954954 char duidbuf [DUID_HEXSTRLEN ], hostname [256 ];
955955
956956 dhcpv6_for_each_option (start , end , otype , olen , odata ) {
957- if (otype == DHCPV6_OPT_CLIENTID ) {
957+ switch (otype ) {
958+ case DHCPV6_OPT_CLIENTID :
958959 duid = odata ;
959960 duid_len = olen ;
960961
@@ -965,17 +966,33 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
965966
966967 if (olen <= DUID_MAX_LEN )
967968 odhcpd_hexlify (duidbuf , odata , olen );
968- } else if (otype == DHCPV6_OPT_FQDN && olen >= 2 && olen <= 255 ) {
969+ break ;
970+
971+ case DHCPV6_OPT_FQDN :
972+ if (olen < 2 || olen > 255 )
973+ break ;
974+
969975 uint8_t fqdn_buf [256 ];
970976 memcpy (fqdn_buf , odata , olen );
971977 fqdn_buf [olen ++ ] = 0 ;
972978
973979 if (dn_expand (& fqdn_buf [1 ], & fqdn_buf [olen ], & fqdn_buf [1 ], hostname , sizeof (hostname )) > 0 )
974980 hostname_len = strcspn (hostname , "." );
975- } else if (otype == DHCPV6_OPT_RECONF_ACCEPT )
981+
982+ break ;
983+
984+ case DHCPV6_OPT_RECONF_ACCEPT :
976985 accept_reconf = true;
977- else if (otype == DHCPV6_OPT_RAPID_COMMIT && hdr -> msg_type == DHCPV6_MSG_SOLICIT )
978- rapid_commit = true;
986+ break ;
987+
988+ case DHCPV6_OPT_RAPID_COMMIT :
989+ if (hdr -> msg_type == DHCPV6_MSG_SOLICIT )
990+ rapid_commit = true;
991+ break ;
992+
993+ default :
994+ break ;
995+ }
979996 }
980997
981998 if (!duid || duid_len < DUID_MIN_LEN || duid_len > DUID_MAX_LEN )
@@ -1091,8 +1108,8 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
10911108 * If there's a DUID configured for this static lease, but without
10921109 * an IAID, we will proceed under the assumption that a request
10931110 * with the right DUID but with *any* IAID should be able to take
1094- * over the assignment. E.g. when switching from WiFi to ethernet
1095- * on the same client. This is similar to how multiple MAC adresses
1111+ * over the assignment. E.g. when switching from WiFi to Ethernet
1112+ * on the same client. This is similar to how multiple MAC addresses
10961113 * are handled for DHCPv4.
10971114 */
10981115 for (size_t i = 0 ; i < lease_cfg -> duid_count ; i ++ ) {
@@ -1135,15 +1152,21 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
11351152proceed :
11361153 /* Generic message handling */
11371154 uint16_t status = DHCPV6_STATUS_OK ;
1155+ bool assigned = false;
1156+
1157+ switch (hdr -> msg_type ) {
1158+ case DHCPV6_MSG_SOLICIT :
1159+ case DHCPV6_MSG_REQUEST :
1160+ case DHCPV6_MSG_REBIND : {
1161+ if (hdr -> msg_type == DHCPV6_MSG_REBIND && a )
1162+ break ;
11381163
1139- if (hdr -> msg_type == DHCPV6_MSG_SOLICIT ||
1140- hdr -> msg_type == DHCPV6_MSG_REQUEST ||
1141- (hdr -> msg_type == DHCPV6_MSG_REBIND && !a )) {
1142- bool assigned = !!a ;
1164+ assigned = (a != NULL );
11431165
11441166 if (!a ) {
11451167 if ((!iface -> no_dynamic_dhcp || (lease_cfg && is_na )) &&
11461168 (iface -> dhcpv6_pd || iface -> dhcpv6_na )) {
1169+
11471170 /* Create new binding */
11481171 a = dhcpv6_alloc_lease (duid_len );
11491172
@@ -1153,25 +1176,27 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
11531176 a -> iaid = ia -> iaid ;
11541177 a -> length = reqlen ;
11551178 a -> peer = * addr ;
1179+ a -> iface = iface ;
1180+ a -> flags = is_pd ? OAF_DHCPV6_PD : OAF_DHCPV6_NA ;
1181+ a -> valid_until = now ;
1182+ a -> preferred_until = now ;
1183+
11561184 if (is_na )
11571185 a -> assigned_host_id = lease_cfg ? lease_cfg -> hostid : 0 ;
11581186 else
11591187 a -> assigned_subnet_id = reqhint ;
1160- a -> valid_until = now ;
1161- a -> preferred_until = now ;
1162- a -> iface = iface ;
1163- a -> flags = (is_pd ? OAF_DHCPV6_PD : OAF_DHCPV6_NA );
11641188
11651189 if (first )
11661190 memcpy (a -> key , first -> key , sizeof (a -> key ));
11671191 else
11681192 odhcpd_urandom (a -> key , sizeof (a -> key ));
11691193
1170- if (is_pd && iface -> dhcpv6_pd )
1194+ if (is_pd && iface -> dhcpv6_pd ) {
11711195 while (!(assigned = assign_pd (iface , a )) &&
11721196 ++ a -> length <= 64 );
1173- else if (is_na && iface -> dhcpv6_na )
1197+ } else if (is_na && iface -> dhcpv6_na ) {
11741198 assigned = assign_na (iface , a );
1199+ }
11751200
11761201 if (lease_cfg && assigned ) {
11771202 if (lease_cfg -> hostname ) {
@@ -1189,15 +1214,20 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
11891214 }
11901215 }
11911216
1192- if (!assigned || iface -> addr6_len == 0 )
1217+ /* Status evaluation */
1218+ if (!assigned || iface -> addr6_len == 0 ) {
11931219 /* Set error status */
1194- status = ( is_pd ) ? DHCPV6_STATUS_NOPREFIXAVAIL : DHCPV6_STATUS_NOADDRSAVAIL ;
1195- else if (hdr -> msg_type == DHCPV6_MSG_REQUEST && !dhcpv6_ia_on_link (ia , a , iface )) {
1220+ status = is_pd ? DHCPV6_STATUS_NOPREFIXAVAIL : DHCPV6_STATUS_NOADDRSAVAIL ;
1221+ } else if (hdr -> msg_type == DHCPV6_MSG_REQUEST && !dhcpv6_ia_on_link (ia , a , iface )) {
11961222 /* Send NOTONLINK status for the IA */
11971223 status = DHCPV6_STATUS_NOTONLINK ;
11981224 assigned = false;
1199- } else if (accept_reconf && assigned && !first &&
1200- hdr -> msg_type != DHCPV6_MSG_REBIND ) {
1225+ }
1226+
1227+ /* Reconfigure Accept */
1228+ if (accept_reconf && assigned && !first &&
1229+ hdr -> msg_type != DHCPV6_MSG_REBIND ) {
1230+
12011231 size_t handshake_len = 4 ;
12021232 buf [0 ] = 0 ;
12031233 buf [1 ] = DHCPV6_OPT_RECONF_ACCEPT ;
@@ -1213,74 +1243,110 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
12131243 1 ,
12141244 {0 }
12151245 };
1246+
12161247 memcpy (auth .key , a -> key , sizeof (a -> key ));
12171248 memcpy (buf + handshake_len , & auth , sizeof (auth ));
12181249 handshake_len += sizeof (auth );
12191250 }
12201251
1221-
12221252 buf += handshake_len ;
12231253 buflen -= handshake_len ;
12241254 response_len += handshake_len ;
12251255
12261256 first = a ;
12271257 }
12281258
1229- ia_response_len = build_ia (buf , buflen , status , ia , a , iface ,
1230- hdr -> msg_type == DHCPV6_MSG_REBIND ? false : true);
1259+ ia_response_len = build_ia (
1260+ buf , buflen , status , ia , a , iface ,
1261+ hdr -> msg_type != DHCPV6_MSG_REBIND );
12311262
12321263 /* Was only a solicitation: mark binding for removal in 60 seconds */
1233- if (assigned && hdr -> msg_type == DHCPV6_MSG_SOLICIT && !rapid_commit ) {
1234- a -> bound = false;
1235- a -> valid_until = now + 60 ;
1236-
1237- } else if (assigned &&
1238- ((hdr -> msg_type == DHCPV6_MSG_SOLICIT && rapid_commit ) ||
1239- hdr -> msg_type == DHCPV6_MSG_REQUEST ||
1240- hdr -> msg_type == DHCPV6_MSG_REBIND )) {
1241- if (hostname_len > 0 && (!a -> lease_cfg || !a -> lease_cfg -> hostname )) {
1242- char * tmp = realloc (a -> hostname , hostname_len + 1 );
1243- if (tmp ) {
1244- a -> hostname = tmp ;
1245- memcpy (a -> hostname , hostname , hostname_len );
1246- a -> hostname [hostname_len ] = 0 ;
1247- a -> hostname_valid = odhcpd_hostname_valid (a -> hostname );
1264+ if (assigned ) {
1265+ switch (hdr -> msg_type ) {
1266+ case DHCPV6_MSG_SOLICIT :
1267+ if (!rapid_commit ) {
1268+ a -> bound = false;
1269+ a -> valid_until = now + 60 ;
1270+ break ;
1271+ }
1272+
1273+ _o_fallthrough ;
1274+ case DHCPV6_MSG_REQUEST :
1275+ case DHCPV6_MSG_REBIND :
1276+ if (hostname_len > 0 && (!a -> lease_cfg || !a -> lease_cfg -> hostname )) {
1277+
1278+ char * tmp = realloc (a -> hostname , hostname_len + 1 );
1279+ if (tmp ) {
1280+ a -> hostname = tmp ;
1281+ memcpy (a -> hostname , hostname , hostname_len );
1282+ a -> hostname [hostname_len ] = 0 ;
1283+ a -> hostname_valid = odhcpd_hostname_valid (a -> hostname );
1284+ }
12481285 }
1286+
1287+ a -> accept_fr_nonce = accept_reconf ;
1288+ a -> bound = true;
1289+ apply_lease (a , true);
1290+ break ;
1291+
1292+ default :
1293+ break ;
12491294 }
1250- a -> accept_fr_nonce = accept_reconf ;
1251- a -> bound = true;
1252- apply_lease (a , true);
1253- } else if (!assigned ) {
1254- /* Cleanup failed assignment */
1295+ } else {
1296+ /* Clean up failed assignment */
12551297 dhcpv6_free_lease (a );
12561298 a = NULL ;
12571299 }
1258- } else if (hdr -> msg_type == DHCPV6_MSG_RENEW ||
1259- hdr -> msg_type == DHCPV6_MSG_RELEASE ||
1260- hdr -> msg_type == DHCPV6_MSG_REBIND ||
1261- hdr -> msg_type == DHCPV6_MSG_DECLINE ) {
1262- if (!a && hdr -> msg_type != DHCPV6_MSG_REBIND ) {
1300+
1301+ break ;
1302+ }
1303+
1304+ case DHCPV6_MSG_RENEW :
1305+ case DHCPV6_MSG_RELEASE :
1306+ case DHCPV6_MSG_DECLINE : {
1307+ /* RENEW / RELEASE / DECLINE require an existing binding */
1308+ if (!a ) {
12631309 status = DHCPV6_STATUS_NOBINDING ;
12641310 ia_response_len = build_ia (buf , buflen , status , ia , a , iface , false);
1265- } else if (hdr -> msg_type == DHCPV6_MSG_RENEW ||
1266- hdr -> msg_type == DHCPV6_MSG_REBIND ) {
1311+ break ;
1312+ }
1313+
1314+ switch (hdr -> msg_type ) {
1315+ case DHCPV6_MSG_RENEW :
12671316 ia_response_len = build_ia (buf , buflen , status , ia , a , iface , false);
1268- if (a ) {
1269- a -> bound = true;
1270- apply_lease (a , true);
1271- }
1272- } else if (hdr -> msg_type == DHCPV6_MSG_RELEASE ) {
1317+
1318+ a -> bound = true;
1319+ apply_lease (a , true);
1320+ break ;
1321+
1322+ case DHCPV6_MSG_RELEASE :
1323+ /* Immediately expire the lease */
12731324 a -> valid_until = now - 1 ;
1274- } else if ((a -> flags & OAF_DHCPV6_NA ) && hdr -> msg_type == DHCPV6_MSG_DECLINE ) {
1325+ break ;
1326+
1327+ case DHCPV6_MSG_DECLINE :
1328+ /* DECLINE only applies to non-temporary addresses */
1329+ if (!(a -> flags & OAF_DHCPV6_NA ))
1330+ break ;
1331+
12751332 a -> bound = false;
12761333
1277- if (!a -> lease_cfg || a -> lease_cfg -> hostid != a -> assigned_host_id ) {
1278- memset (a -> duid , 0 , a -> duid_len );
1279- a -> valid_until = now + 3600 ; /* Block address for 1h */
1280- } else
1334+ if (a -> lease_cfg &&
1335+ a -> lease_cfg -> hostid == a -> assigned_host_id ) {
1336+ /* Static lease: release immediately */
12811337 a -> valid_until = now - 1 ;
1338+ } else {
1339+ /* Dynamic lease: block address for 1 hour */
1340+ memset (a -> duid , 0 , a -> duid_len );
1341+ a -> valid_until = now + 3600 ;
1342+ }
1343+ break ;
12821344 }
1283- } else if (hdr -> msg_type == DHCPV6_MSG_CONFIRM ) {
1345+
1346+ break ;
1347+ }
1348+
1349+ case DHCPV6_MSG_CONFIRM :
12841350 if (ia_addr_present && !dhcpv6_ia_on_link (ia , a , iface )) {
12851351 notonlink = true;
12861352 break ;
@@ -1290,13 +1356,23 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
12901356 response_len = 0 ;
12911357 goto out ;
12921358 }
1359+ break ;
1360+
1361+ default :
1362+ break ;
1363+ }
1364+
1365+ if (hdr -> msg_type == DHCPV6_MSG_REBIND && a ) {
1366+ ia_response_len = build_ia (buf , buflen , status , ia , a , iface , false);
1367+ a -> bound = true;
1368+ apply_lease (a , true);
12931369 }
12941370
12951371 buf += ia_response_len ;
12961372 buflen -= ia_response_len ;
12971373 response_len += ia_response_len ;
12981374 dhcpv6_log (hdr -> msg_type , iface , now , duidbuf , is_pd , a , status );
1299- }
1375+ } /* end dhcpv6_for_each_option */
13001376
13011377 switch (hdr -> msg_type ) {
13021378 case DHCPV6_MSG_RELEASE :
0 commit comments