@@ -958,7 +958,8 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
958958 char duidbuf [DUID_HEXSTRLEN ], hostname [256 ];
959959
960960 dhcpv6_for_each_option (start , end , otype , olen , odata ) {
961- if (otype == DHCPV6_OPT_CLIENTID ) {
961+ switch (otype ) {
962+ case DHCPV6_OPT_CLIENTID :
962963 duid = odata ;
963964 duid_len = olen ;
964965
@@ -969,17 +970,33 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
969970
970971 if (olen <= DUID_MAX_LEN )
971972 odhcpd_hexlify (duidbuf , odata , olen );
972- } else if (otype == DHCPV6_OPT_FQDN && olen >= 2 && olen <= 255 ) {
973+ break ;
974+
975+ case DHCPV6_OPT_FQDN :
976+ if (olen < 2 || olen > 255 )
977+ break ;
978+
973979 uint8_t fqdn_buf [256 ];
974980 memcpy (fqdn_buf , odata , olen );
975981 fqdn_buf [olen ++ ] = 0 ;
976982
977983 if (dn_expand (& fqdn_buf [1 ], & fqdn_buf [olen ], & fqdn_buf [1 ], hostname , sizeof (hostname )) > 0 )
978984 hostname_len = strcspn (hostname , "." );
979- } else if (otype == DHCPV6_OPT_RECONF_ACCEPT )
985+
986+ break ;
987+
988+ case DHCPV6_OPT_RECONF_ACCEPT :
980989 accept_reconf = true;
981- else if (otype == DHCPV6_OPT_RAPID_COMMIT && hdr -> msg_type == DHCPV6_MSG_SOLICIT )
982- rapid_commit = true;
990+ break ;
991+
992+ case DHCPV6_OPT_RAPID_COMMIT :
993+ if (hdr -> msg_type == DHCPV6_MSG_SOLICIT )
994+ rapid_commit = true;
995+ break ;
996+
997+ default :
998+ break ;
999+ }
9831000 }
9841001
9851002 if (!duid || duid_len < DUID_MIN_LEN || duid_len > DUID_MAX_LEN )
@@ -1095,8 +1112,8 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
10951112 * If there's a DUID configured for this static lease, but without
10961113 * an IAID, we will proceed under the assumption that a request
10971114 * with the right DUID but with *any* IAID should be able to take
1098- * over the assignment. E.g. when switching from WiFi to ethernet
1099- * on the same client. This is similar to how multiple MAC adresses
1115+ * over the assignment. E.g. when switching from WiFi to Ethernet
1116+ * on the same client. This is similar to how multiple MAC addresses
11001117 * are handled for DHCPv4.
11011118 */
11021119 for (size_t i = 0 ; i < lease_cfg -> duid_count ; i ++ ) {
@@ -1139,15 +1156,21 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
11391156proceed :
11401157 /* Generic message handling */
11411158 uint16_t status = DHCPV6_STATUS_OK ;
1159+ bool assigned = false;
1160+
1161+ switch (hdr -> msg_type ) {
1162+ case DHCPV6_MSG_SOLICIT :
1163+ case DHCPV6_MSG_REQUEST :
1164+ case DHCPV6_MSG_REBIND : {
1165+ if (hdr -> msg_type == DHCPV6_MSG_REBIND && a )
1166+ break ;
11421167
1143- if (hdr -> msg_type == DHCPV6_MSG_SOLICIT ||
1144- hdr -> msg_type == DHCPV6_MSG_REQUEST ||
1145- (hdr -> msg_type == DHCPV6_MSG_REBIND && !a )) {
1146- bool assigned = !!a ;
1168+ assigned = (a != NULL );
11471169
11481170 if (!a ) {
11491171 if ((!iface -> no_dynamic_dhcp || (lease_cfg && is_na )) &&
11501172 (iface -> dhcpv6_pd || iface -> dhcpv6_na )) {
1173+
11511174 /* Create new binding */
11521175 a = dhcpv6_alloc_lease (duid_len );
11531176
@@ -1157,25 +1180,27 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
11571180 a -> iaid = ia -> iaid ;
11581181 a -> length = reqlen ;
11591182 a -> peer = * addr ;
1183+ a -> iface = iface ;
1184+ a -> flags = is_pd ? OAF_DHCPV6_PD : OAF_DHCPV6_NA ;
1185+ a -> valid_until = now ;
1186+ a -> preferred_until = now ;
1187+
11601188 if (is_na )
11611189 a -> assigned_host_id = lease_cfg ? lease_cfg -> hostid : 0 ;
11621190 else
11631191 a -> assigned_subnet_id = reqhint ;
1164- a -> valid_until = now ;
1165- a -> preferred_until = now ;
1166- a -> iface = iface ;
1167- a -> flags = (is_pd ? OAF_DHCPV6_PD : OAF_DHCPV6_NA );
11681192
11691193 if (first )
11701194 memcpy (a -> key , first -> key , sizeof (a -> key ));
11711195 else
11721196 odhcpd_urandom (a -> key , sizeof (a -> key ));
11731197
1174- if (is_pd && iface -> dhcpv6_pd )
1198+ if (is_pd && iface -> dhcpv6_pd ) {
11751199 while (!(assigned = assign_pd (iface , a )) &&
11761200 ++ a -> length <= 64 );
1177- else if (is_na && iface -> dhcpv6_na )
1201+ } else if (is_na && iface -> dhcpv6_na ) {
11781202 assigned = assign_na (iface , a );
1203+ }
11791204
11801205 if (lease_cfg && assigned ) {
11811206 if (lease_cfg -> hostname ) {
@@ -1193,15 +1218,20 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
11931218 }
11941219 }
11951220
1196- if (!assigned || iface -> addr6_len == 0 )
1221+ /* Status evaluation */
1222+ if (!assigned || iface -> addr6_len == 0 ) {
11971223 /* Set error status */
1198- status = ( is_pd ) ? DHCPV6_STATUS_NOPREFIXAVAIL : DHCPV6_STATUS_NOADDRSAVAIL ;
1199- else if (hdr -> msg_type == DHCPV6_MSG_REQUEST && !dhcpv6_ia_on_link (ia , a , iface )) {
1224+ status = is_pd ? DHCPV6_STATUS_NOPREFIXAVAIL : DHCPV6_STATUS_NOADDRSAVAIL ;
1225+ } else if (hdr -> msg_type == DHCPV6_MSG_REQUEST && !dhcpv6_ia_on_link (ia , a , iface )) {
12001226 /* Send NOTONLINK status for the IA */
12011227 status = DHCPV6_STATUS_NOTONLINK ;
12021228 assigned = false;
1203- } else if (accept_reconf && assigned && !first &&
1204- hdr -> msg_type != DHCPV6_MSG_REBIND ) {
1229+ }
1230+
1231+ /* Reconfigure Accept */
1232+ if (accept_reconf && assigned && !first &&
1233+ hdr -> msg_type != DHCPV6_MSG_REBIND ) {
1234+
12051235 size_t handshake_len = 4 ;
12061236 buf [0 ] = 0 ;
12071237 buf [1 ] = DHCPV6_OPT_RECONF_ACCEPT ;
@@ -1217,74 +1247,110 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
12171247 1 ,
12181248 {0 }
12191249 };
1250+
12201251 memcpy (auth .key , a -> key , sizeof (a -> key ));
12211252 memcpy (buf + handshake_len , & auth , sizeof (auth ));
12221253 handshake_len += sizeof (auth );
12231254 }
12241255
1225-
12261256 buf += handshake_len ;
12271257 buflen -= handshake_len ;
12281258 response_len += handshake_len ;
12291259
12301260 first = a ;
12311261 }
12321262
1233- ia_response_len = build_ia (buf , buflen , status , ia , a , iface ,
1234- hdr -> msg_type == DHCPV6_MSG_REBIND ? false : true);
1263+ ia_response_len = build_ia (
1264+ buf , buflen , status , ia , a , iface ,
1265+ hdr -> msg_type != DHCPV6_MSG_REBIND );
12351266
12361267 /* Was only a solicitation: mark binding for removal in 60 seconds */
1237- if (assigned && hdr -> msg_type == DHCPV6_MSG_SOLICIT && !rapid_commit ) {
1238- a -> bound = false;
1239- a -> valid_until = now + 60 ;
1240-
1241- } else if (assigned &&
1242- ((hdr -> msg_type == DHCPV6_MSG_SOLICIT && rapid_commit ) ||
1243- hdr -> msg_type == DHCPV6_MSG_REQUEST ||
1244- hdr -> msg_type == DHCPV6_MSG_REBIND )) {
1245- if (hostname_len > 0 && (!a -> lease_cfg || !a -> lease_cfg -> hostname )) {
1246- char * tmp = realloc (a -> hostname , hostname_len + 1 );
1247- if (tmp ) {
1248- a -> hostname = tmp ;
1249- memcpy (a -> hostname , hostname , hostname_len );
1250- a -> hostname [hostname_len ] = 0 ;
1251- a -> hostname_valid = odhcpd_hostname_valid (a -> hostname );
1268+ if (assigned ) {
1269+ switch (hdr -> msg_type ) {
1270+ case DHCPV6_MSG_SOLICIT :
1271+ if (!rapid_commit ) {
1272+ a -> bound = false;
1273+ a -> valid_until = now + 60 ;
1274+ break ;
1275+ }
1276+
1277+ _o_fallthrough ;
1278+ case DHCPV6_MSG_REQUEST :
1279+ case DHCPV6_MSG_REBIND :
1280+ if (hostname_len > 0 && (!a -> lease_cfg || !a -> lease_cfg -> hostname )) {
1281+
1282+ char * tmp = realloc (a -> hostname , hostname_len + 1 );
1283+ if (tmp ) {
1284+ a -> hostname = tmp ;
1285+ memcpy (a -> hostname , hostname , hostname_len );
1286+ a -> hostname [hostname_len ] = 0 ;
1287+ a -> hostname_valid = odhcpd_hostname_valid (a -> hostname );
1288+ }
12521289 }
1290+
1291+ a -> accept_fr_nonce = accept_reconf ;
1292+ a -> bound = true;
1293+ apply_lease (a , true);
1294+ break ;
1295+
1296+ default :
1297+ break ;
12531298 }
1254- a -> accept_fr_nonce = accept_reconf ;
1255- a -> bound = true;
1256- apply_lease (a , true);
1257- } else if (!assigned ) {
1258- /* Cleanup failed assignment */
1299+ } else {
1300+ /* Clean up failed assignment */
12591301 dhcpv6_free_lease (a );
12601302 a = NULL ;
12611303 }
1262- } else if (hdr -> msg_type == DHCPV6_MSG_RENEW ||
1263- hdr -> msg_type == DHCPV6_MSG_RELEASE ||
1264- hdr -> msg_type == DHCPV6_MSG_REBIND ||
1265- hdr -> msg_type == DHCPV6_MSG_DECLINE ) {
1266- if (!a && hdr -> msg_type != DHCPV6_MSG_REBIND ) {
1304+
1305+ break ;
1306+ }
1307+
1308+ case DHCPV6_MSG_RENEW :
1309+ case DHCPV6_MSG_RELEASE :
1310+ case DHCPV6_MSG_DECLINE : {
1311+ /* RENEW / RELEASE / DECLINE require an existing binding */
1312+ if (!a ) {
12671313 status = DHCPV6_STATUS_NOBINDING ;
12681314 ia_response_len = build_ia (buf , buflen , status , ia , a , iface , false);
1269- } else if (hdr -> msg_type == DHCPV6_MSG_RENEW ||
1270- hdr -> msg_type == DHCPV6_MSG_REBIND ) {
1315+ break ;
1316+ }
1317+
1318+ switch (hdr -> msg_type ) {
1319+ case DHCPV6_MSG_RENEW :
12711320 ia_response_len = build_ia (buf , buflen , status , ia , a , iface , false);
1272- if (a ) {
1273- a -> bound = true;
1274- apply_lease (a , true);
1275- }
1276- } else if (hdr -> msg_type == DHCPV6_MSG_RELEASE ) {
1321+
1322+ a -> bound = true;
1323+ apply_lease (a , true);
1324+ break ;
1325+
1326+ case DHCPV6_MSG_RELEASE :
1327+ /* Immediately expire the lease */
12771328 a -> valid_until = now - 1 ;
1278- } else if ((a -> flags & OAF_DHCPV6_NA ) && hdr -> msg_type == DHCPV6_MSG_DECLINE ) {
1329+ break ;
1330+
1331+ case DHCPV6_MSG_DECLINE :
1332+ /* DECLINE only applies to non-temporary addresses */
1333+ if (!(a -> flags & OAF_DHCPV6_NA ))
1334+ break ;
1335+
12791336 a -> bound = false;
12801337
1281- if (!a -> lease_cfg || a -> lease_cfg -> hostid != a -> assigned_host_id ) {
1282- memset (a -> duid , 0 , a -> duid_len );
1283- a -> valid_until = now + 3600 ; /* Block address for 1h */
1284- } else
1338+ if (a -> lease_cfg &&
1339+ a -> lease_cfg -> hostid == a -> assigned_host_id ) {
1340+ /* Static lease: release immediately */
12851341 a -> valid_until = now - 1 ;
1342+ } else {
1343+ /* Dynamic lease: block address for 1 hour */
1344+ memset (a -> duid , 0 , a -> duid_len );
1345+ a -> valid_until = now + 3600 ;
1346+ }
1347+ break ;
12861348 }
1287- } else if (hdr -> msg_type == DHCPV6_MSG_CONFIRM ) {
1349+
1350+ break ;
1351+ }
1352+
1353+ case DHCPV6_MSG_CONFIRM :
12881354 if (ia_addr_present && !dhcpv6_ia_on_link (ia , a , iface )) {
12891355 notonlink = true;
12901356 break ;
@@ -1294,13 +1360,23 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
12941360 response_len = 0 ;
12951361 goto out ;
12961362 }
1363+ break ;
1364+
1365+ default :
1366+ break ;
1367+ }
1368+
1369+ if (hdr -> msg_type == DHCPV6_MSG_REBIND && a ) {
1370+ ia_response_len = build_ia (buf , buflen , status , ia , a , iface , false);
1371+ a -> bound = true;
1372+ apply_lease (a , true);
12971373 }
12981374
12991375 buf += ia_response_len ;
13001376 buflen -= ia_response_len ;
13011377 response_len += ia_response_len ;
13021378 dhcpv6_log (hdr -> msg_type , iface , now , duidbuf , is_pd , a , status );
1303- }
1379+ } /* end dhcpv6_for_each_option */
13041380
13051381 switch (hdr -> msg_type ) {
13061382 case DHCPV6_MSG_RELEASE :
0 commit comments