|
@@ -175,11 +175,12 @@ struct dhcp_session_state {
|
175
|
175
|
* @v dhcppkt DHCP packet
|
176
|
176
|
* @v peer DHCP server address
|
177
|
177
|
* @v msgtype DHCP message type
|
|
178
|
+ * @v server_id DHCP server ID
|
178
|
179
|
*/
|
179
|
180
|
void ( * rx ) ( struct dhcp_session *dhcp,
|
180
|
181
|
struct dhcp_packet *dhcppkt,
|
181
|
182
|
struct sockaddr_in *peer,
|
182
|
|
- uint8_t msgtype );
|
|
183
|
+ uint8_t msgtype, struct in_addr server_id );
|
183
|
184
|
/** Handle timer expiry
|
184
|
185
|
*
|
185
|
186
|
* @v dhcp DHCP session
|
|
@@ -226,10 +227,12 @@ struct dhcp_session {
|
226
|
227
|
/** ProxyDHCP server priority */
|
227
|
228
|
int proxy_priority;
|
228
|
229
|
|
229
|
|
- /** PXE Boot Server */
|
230
|
|
- struct in_addr pxe_server;
|
231
|
230
|
/** PXE Boot Server type */
|
232
|
231
|
uint16_t pxe_type;
|
|
232
|
+ /** List of PXE Boot Servers to attempt */
|
|
233
|
+ struct in_addr *pxe_attempt;
|
|
234
|
+ /** List of PXE Boot Servers to accept */
|
|
235
|
+ struct in_addr *pxe_accept;
|
233
|
236
|
|
234
|
237
|
/** Retransmission timer */
|
235
|
238
|
struct retry_timer timer;
|
|
@@ -322,11 +325,12 @@ static int dhcp_discovery_tx ( struct dhcp_session *dhcp,
|
322
|
325
|
* @v dhcppkt DHCP packet
|
323
|
326
|
* @v peer DHCP server address
|
324
|
327
|
* @v msgtype DHCP message type
|
|
328
|
+ * @v server_id DHCP server ID
|
325
|
329
|
*/
|
326
|
330
|
static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
|
327
|
331
|
struct dhcp_packet *dhcppkt,
|
328
|
|
- struct sockaddr_in *peer, uint8_t msgtype ) {
|
329
|
|
- struct in_addr server_id = { 0 };
|
|
332
|
+ struct sockaddr_in *peer, uint8_t msgtype,
|
|
333
|
+ struct in_addr server_id ) {
|
330
|
334
|
struct in_addr ip;
|
331
|
335
|
char vci[9]; /* "PXEClient" */
|
332
|
336
|
int vci_len;
|
|
@@ -338,10 +342,6 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
|
338
|
342
|
DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
|
339
|
343
|
dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
|
340
|
344
|
ntohs ( peer->sin_port ) );
|
341
|
|
-
|
342
|
|
- /* Identify server ID */
|
343
|
|
- dhcppkt_fetch ( dhcppkt, DHCP_SERVER_IDENTIFIER,
|
344
|
|
- &server_id, sizeof ( server_id ) );
|
345
|
345
|
if ( server_id.s_addr != peer->sin_addr.s_addr )
|
346
|
346
|
DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) );
|
347
|
347
|
|
|
@@ -480,11 +480,12 @@ static int dhcp_request_tx ( struct dhcp_session *dhcp,
|
480
|
480
|
* @v dhcppkt DHCP packet
|
481
|
481
|
* @v peer DHCP server address
|
482
|
482
|
* @v msgtype DHCP message type
|
|
483
|
+ * @v server_id DHCP server ID
|
483
|
484
|
*/
|
484
|
485
|
static void dhcp_request_rx ( struct dhcp_session *dhcp,
|
485
|
486
|
struct dhcp_packet *dhcppkt,
|
486
|
|
- struct sockaddr_in *peer, uint8_t msgtype ) {
|
487
|
|
- struct in_addr server_id = { 0 };
|
|
487
|
+ struct sockaddr_in *peer, uint8_t msgtype,
|
|
488
|
+ struct in_addr server_id ) {
|
488
|
489
|
struct in_addr ip;
|
489
|
490
|
struct settings *parent;
|
490
|
491
|
int rc;
|
|
@@ -492,10 +493,6 @@ static void dhcp_request_rx ( struct dhcp_session *dhcp,
|
492
|
493
|
DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
|
493
|
494
|
dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
|
494
|
495
|
ntohs ( peer->sin_port ) );
|
495
|
|
-
|
496
|
|
- /* Identify server ID */
|
497
|
|
- dhcppkt_fetch ( dhcppkt, DHCP_SERVER_IDENTIFIER,
|
498
|
|
- &server_id, sizeof ( server_id ) );
|
499
|
496
|
if ( server_id.s_addr != peer->sin_addr.s_addr )
|
500
|
497
|
DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) );
|
501
|
498
|
|
|
@@ -591,20 +588,17 @@ static int dhcp_proxy_tx ( struct dhcp_session *dhcp,
|
591
|
588
|
* @v dhcppkt DHCP packet
|
592
|
589
|
* @v peer DHCP server address
|
593
|
590
|
* @v msgtype DHCP message type
|
|
591
|
+ * @v server_id DHCP server ID
|
594
|
592
|
*/
|
595
|
593
|
static void dhcp_proxy_rx ( struct dhcp_session *dhcp,
|
596
|
594
|
struct dhcp_packet *dhcppkt,
|
597
|
|
- struct sockaddr_in *peer, uint8_t msgtype ) {
|
598
|
|
- struct in_addr server_id = { 0 };
|
|
595
|
+ struct sockaddr_in *peer, uint8_t msgtype,
|
|
596
|
+ struct in_addr server_id ) {
|
599
|
597
|
int rc;
|
600
|
598
|
|
601
|
599
|
DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
|
602
|
600
|
dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
|
603
|
601
|
ntohs ( peer->sin_port ) );
|
604
|
|
-
|
605
|
|
- /* Identify server ID */
|
606
|
|
- dhcppkt_fetch ( dhcppkt, DHCP_SERVER_IDENTIFIER,
|
607
|
|
- &server_id, sizeof ( server_id ) );
|
608
|
602
|
if ( server_id.s_addr != peer->sin_addr.s_addr )
|
609
|
603
|
DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) );
|
610
|
604
|
DBGC ( dhcp, "\n" );
|
|
@@ -673,7 +667,7 @@ static int dhcp_pxebs_tx ( struct dhcp_session *dhcp,
|
673
|
667
|
int rc;
|
674
|
668
|
|
675
|
669
|
DBGC ( dhcp, "DHCP %p PXEBS REQUEST to %s:%d for type %d\n",
|
676
|
|
- dhcp, inet_ntoa ( dhcp->pxe_server ), PXE_PORT,
|
|
670
|
+ dhcp, inet_ntoa ( *(dhcp->pxe_attempt) ), PXE_PORT,
|
677
|
671
|
ntohs ( dhcp->pxe_type ) );
|
678
|
672
|
|
679
|
673
|
/* Set boot menu item */
|
|
@@ -683,12 +677,38 @@ static int dhcp_pxebs_tx ( struct dhcp_session *dhcp,
|
683
|
677
|
return rc;
|
684
|
678
|
|
685
|
679
|
/* Set server address */
|
686
|
|
- peer->sin_addr = dhcp->pxe_server;
|
|
680
|
+ peer->sin_addr = *(dhcp->pxe_attempt);
|
687
|
681
|
peer->sin_port = htons ( PXE_PORT );
|
688
|
682
|
|
689
|
683
|
return 0;
|
690
|
684
|
}
|
691
|
685
|
|
|
686
|
+/**
|
|
687
|
+ * Check to see if PXE Boot Server address is acceptable
|
|
688
|
+ *
|
|
689
|
+ * @v dhcp DHCP session
|
|
690
|
+ * @v bs Boot Server address
|
|
691
|
+ * @ret accept Boot Server is acceptable
|
|
692
|
+ */
|
|
693
|
+static int dhcp_pxebs_accept ( struct dhcp_session *dhcp,
|
|
694
|
+ struct in_addr bs ) {
|
|
695
|
+ struct in_addr *accept;
|
|
696
|
+
|
|
697
|
+ /* Accept if we have no acceptance filter */
|
|
698
|
+ if ( ! dhcp->pxe_accept )
|
|
699
|
+ return 1;
|
|
700
|
+
|
|
701
|
+ /* Scan through acceptance list */
|
|
702
|
+ for ( accept = dhcp->pxe_accept ; accept->s_addr ; accept++ ) {
|
|
703
|
+ if ( accept->s_addr == bs.s_addr )
|
|
704
|
+ return 1;
|
|
705
|
+ }
|
|
706
|
+
|
|
707
|
+ DBGC ( dhcp, "DHCP %p rejecting server %s\n",
|
|
708
|
+ dhcp, inet_ntoa ( bs ) );
|
|
709
|
+ return 0;
|
|
710
|
+}
|
|
711
|
+
|
692
|
712
|
/**
|
693
|
713
|
* Handle received packet during PXE Boot Server Discovery
|
694
|
714
|
*
|
|
@@ -696,16 +716,20 @@ static int dhcp_pxebs_tx ( struct dhcp_session *dhcp,
|
696
|
716
|
* @v dhcppkt DHCP packet
|
697
|
717
|
* @v peer DHCP server address
|
698
|
718
|
* @v msgtype DHCP message type
|
|
719
|
+ * @v server_id DHCP server ID
|
699
|
720
|
*/
|
700
|
721
|
static void dhcp_pxebs_rx ( struct dhcp_session *dhcp,
|
701
|
722
|
struct dhcp_packet *dhcppkt,
|
702
|
|
- struct sockaddr_in *peer, uint8_t msgtype ) {
|
|
723
|
+ struct sockaddr_in *peer, uint8_t msgtype,
|
|
724
|
+ struct in_addr server_id ) {
|
703
|
725
|
struct dhcp_pxe_boot_menu_item menu_item = { 0, 0 };
|
704
|
726
|
int rc;
|
705
|
727
|
|
706
|
728
|
DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
|
707
|
729
|
dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
|
708
|
730
|
ntohs ( peer->sin_port ) );
|
|
731
|
+ if ( server_id.s_addr != peer->sin_addr.s_addr )
|
|
732
|
+ DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) );
|
709
|
733
|
|
710
|
734
|
/* Identify boot menu item */
|
711
|
735
|
dhcppkt_fetch ( dhcppkt, DHCP_PXE_BOOT_MENU_ITEM,
|
|
@@ -721,6 +745,9 @@ static void dhcp_pxebs_rx ( struct dhcp_session *dhcp,
|
721
|
745
|
return;
|
722
|
746
|
if ( menu_item.type != dhcp->pxe_type )
|
723
|
747
|
return;
|
|
748
|
+ if ( ! dhcp_pxebs_accept ( dhcp, ( server_id.s_addr ?
|
|
749
|
+ server_id : peer->sin_addr ) ) )
|
|
750
|
+ return;
|
724
|
751
|
|
725
|
752
|
/* Register settings */
|
726
|
753
|
dhcppkt->settings.name = PXEBS_SETTINGS_NAME;
|
|
@@ -741,6 +768,21 @@ static void dhcp_pxebs_rx ( struct dhcp_session *dhcp,
|
741
|
768
|
* @v dhcp DHCP session
|
742
|
769
|
*/
|
743
|
770
|
static void dhcp_pxebs_expired ( struct dhcp_session *dhcp ) {
|
|
771
|
+ unsigned long elapsed = ( currticks() - dhcp->start );
|
|
772
|
+
|
|
773
|
+ /* Give up waiting before we reach the failure point, and fail
|
|
774
|
+ * over to the next server in the attempt list
|
|
775
|
+ */
|
|
776
|
+ if ( elapsed > PXEBS_MAX_TIMEOUT ) {
|
|
777
|
+ dhcp->pxe_attempt++;
|
|
778
|
+ if ( dhcp->pxe_attempt->s_addr ) {
|
|
779
|
+ dhcp_set_state ( dhcp, &dhcp_state_pxebs );
|
|
780
|
+ return;
|
|
781
|
+ } else {
|
|
782
|
+ dhcp_finished ( dhcp, -ETIMEDOUT );
|
|
783
|
+ return;
|
|
784
|
+ }
|
|
785
|
+ }
|
744
|
786
|
|
745
|
787
|
/* Retransmit current packet */
|
746
|
788
|
dhcp_tx ( dhcp );
|
|
@@ -1006,6 +1048,7 @@ static int dhcp_deliver_iob ( struct xfer_interface *xfer,
|
1006
|
1048
|
struct dhcp_packet *dhcppkt;
|
1007
|
1049
|
struct dhcphdr *dhcphdr;
|
1008
|
1050
|
uint8_t msgtype = 0;
|
|
1051
|
+ struct in_addr server_id = { 0 };
|
1009
|
1052
|
int rc = 0;
|
1010
|
1053
|
|
1011
|
1054
|
/* Sanity checks */
|
|
@@ -1042,6 +1085,10 @@ static int dhcp_deliver_iob ( struct xfer_interface *xfer,
|
1042
|
1085
|
dhcppkt_fetch ( dhcppkt, DHCP_MESSAGE_TYPE, &msgtype,
|
1043
|
1086
|
sizeof ( msgtype ) );
|
1044
|
1087
|
|
|
1088
|
+ /* Identify server ID */
|
|
1089
|
+ dhcppkt_fetch ( dhcppkt, DHCP_SERVER_IDENTIFIER,
|
|
1090
|
+ &server_id, sizeof ( server_id ) );
|
|
1091
|
+
|
1045
|
1092
|
/* Check for matching transaction ID */
|
1046
|
1093
|
if ( dhcphdr->xid != dhcp_xid ( dhcp->netdev ) ) {
|
1047
|
1094
|
DBGC ( dhcp, "DHCP %p %s from %s:%d has bad transaction "
|
|
@@ -1053,7 +1100,7 @@ static int dhcp_deliver_iob ( struct xfer_interface *xfer,
|
1053
|
1100
|
};
|
1054
|
1101
|
|
1055
|
1102
|
/* Handle packet based on current state */
|
1056
|
|
- dhcp->state->rx ( dhcp, dhcppkt, peer, msgtype );
|
|
1103
|
+ dhcp->state->rx ( dhcp, dhcppkt, peer, msgtype, server_id );
|
1057
|
1104
|
|
1058
|
1105
|
err_xid:
|
1059
|
1106
|
dhcppkt_put ( dhcppkt );
|
|
@@ -1183,12 +1230,50 @@ int start_dhcp ( struct job_interface *job, struct net_device *netdev ) {
|
1183
|
1230
|
return rc;
|
1184
|
1231
|
}
|
1185
|
1232
|
|
|
1233
|
+/**
|
|
1234
|
+ * Retrieve list of PXE boot servers for a given server type
|
|
1235
|
+ *
|
|
1236
|
+ * @v dhcp DHCP session
|
|
1237
|
+ * @v raw DHCP PXE boot server list
|
|
1238
|
+ * @v raw_len Length of DHCP PXE boot server list
|
|
1239
|
+ * @v ip IP address list to fill in
|
|
1240
|
+ *
|
|
1241
|
+ * The caller must ensure that the IP address list has sufficient
|
|
1242
|
+ * space.
|
|
1243
|
+ */
|
|
1244
|
+static void pxebs_list ( struct dhcp_session *dhcp, void *raw,
|
|
1245
|
+ size_t raw_len, struct in_addr *ip ) {
|
|
1246
|
+ struct dhcp_pxe_boot_server *server = raw;
|
|
1247
|
+ size_t server_len;
|
|
1248
|
+ unsigned int i;
|
|
1249
|
+
|
|
1250
|
+ while ( raw_len ) {
|
|
1251
|
+ if ( raw_len < sizeof ( *server ) ) {
|
|
1252
|
+ DBGC ( dhcp, "DHCP %p malformed PXE server list\n",
|
|
1253
|
+ dhcp );
|
|
1254
|
+ break;
|
|
1255
|
+ }
|
|
1256
|
+ server_len = offsetof ( typeof ( *server ),
|
|
1257
|
+ ip[ server->num_ip ] );
|
|
1258
|
+ if ( raw_len < server_len ) {
|
|
1259
|
+ DBGC ( dhcp, "DHCP %p malformed PXE server list\n",
|
|
1260
|
+ dhcp );
|
|
1261
|
+ break;
|
|
1262
|
+ }
|
|
1263
|
+ if ( server->type == dhcp->pxe_type ) {
|
|
1264
|
+ for ( i = 0 ; i < server->num_ip ; i++ )
|
|
1265
|
+ *(ip++) = server->ip[i];
|
|
1266
|
+ }
|
|
1267
|
+ server = ( ( ( void * ) server ) + server_len );
|
|
1268
|
+ raw_len -= server_len;
|
|
1269
|
+ }
|
|
1270
|
+}
|
|
1271
|
+
|
1186
|
1272
|
/**
|
1187
|
1273
|
* Start PXE Boot Server Discovery on a network device
|
1188
|
1274
|
*
|
1189
|
1275
|
* @v job Job control interface
|
1190
|
1276
|
* @v netdev Network device
|
1191
|
|
- * @v pxe_server PXE server (may be a multicast address)
|
1192
|
1277
|
* @v pxe_type PXE server type
|
1193
|
1278
|
* @ret rc Return status code
|
1194
|
1279
|
*
|
|
@@ -1197,12 +1282,28 @@ int start_dhcp ( struct job_interface *job, struct net_device *netdev ) {
|
1197
|
1282
|
* source.
|
1198
|
1283
|
*/
|
1199
|
1284
|
int start_pxebs ( struct job_interface *job, struct net_device *netdev,
|
1200
|
|
- struct in_addr pxe_server, unsigned int pxe_type ) {
|
|
1285
|
+ unsigned int pxe_type ) {
|
|
1286
|
+ struct setting pxe_discovery_control_setting =
|
|
1287
|
+ { .tag = DHCP_PXE_DISCOVERY_CONTROL };
|
|
1288
|
+ struct setting pxe_boot_servers_setting =
|
|
1289
|
+ { .tag = DHCP_PXE_BOOT_SERVERS };
|
|
1290
|
+ struct setting pxe_boot_server_mcast_setting =
|
|
1291
|
+ { .tag = DHCP_PXE_BOOT_SERVER_MCAST };
|
|
1292
|
+ ssize_t pxebs_list_len;
|
1201
|
1293
|
struct dhcp_session *dhcp;
|
|
1294
|
+ struct in_addr *ip;
|
|
1295
|
+ unsigned int pxe_discovery_control;
|
1202
|
1296
|
int rc;
|
1203
|
1297
|
|
|
1298
|
+ /* Get upper bound for PXE boot server IP address list */
|
|
1299
|
+ pxebs_list_len = fetch_setting_len ( NULL, &pxe_boot_servers_setting );
|
|
1300
|
+ if ( pxebs_list_len < 0 )
|
|
1301
|
+ pxebs_list_len = 0;
|
|
1302
|
+
|
1204
|
1303
|
/* Allocate and initialise structure */
|
1205
|
|
- dhcp = zalloc ( sizeof ( *dhcp ) );
|
|
1304
|
+ dhcp = zalloc ( sizeof ( *dhcp ) + sizeof ( *ip ) /* mcast */ +
|
|
1305
|
+ sizeof ( *ip ) /* bcast */ + pxebs_list_len +
|
|
1306
|
+ sizeof ( *ip ) /* terminator */ );
|
1206
|
1307
|
if ( ! dhcp )
|
1207
|
1308
|
return -ENOMEM;
|
1208
|
1309
|
dhcp->refcnt.free = dhcp_free;
|
|
@@ -1213,10 +1314,49 @@ int start_pxebs ( struct job_interface *job, struct net_device *netdev,
|
1213
|
1314
|
fetch_ipv4_setting ( netdev_settings ( netdev ), &ip_setting,
|
1214
|
1315
|
&dhcp->local.sin_addr );
|
1215
|
1316
|
dhcp->local.sin_port = htons ( BOOTPC_PORT );
|
1216
|
|
- dhcp->pxe_server = pxe_server;
|
1217
|
1317
|
dhcp->pxe_type = htons ( pxe_type );
|
1218
|
1318
|
dhcp->timer.expired = dhcp_timer_expired;
|
1219
|
1319
|
|
|
1320
|
+ /* Construct PXE boot server IP address lists */
|
|
1321
|
+ pxe_discovery_control =
|
|
1322
|
+ fetch_uintz_setting ( NULL, &pxe_discovery_control_setting );
|
|
1323
|
+ ip = ( ( ( void * ) dhcp ) + sizeof ( *dhcp ) );
|
|
1324
|
+ dhcp->pxe_attempt = ip;
|
|
1325
|
+ if ( ! ( pxe_discovery_control & PXEBS_NO_MULTICAST ) ) {
|
|
1326
|
+ fetch_ipv4_setting ( NULL, &pxe_boot_server_mcast_setting, ip);
|
|
1327
|
+ if ( ip->s_addr )
|
|
1328
|
+ ip++;
|
|
1329
|
+ }
|
|
1330
|
+ if ( ! ( pxe_discovery_control & PXEBS_NO_BROADCAST ) )
|
|
1331
|
+ (ip++)->s_addr = INADDR_BROADCAST;
|
|
1332
|
+ if ( pxe_discovery_control & PXEBS_NO_UNKNOWN_SERVERS )
|
|
1333
|
+ dhcp->pxe_accept = ip;
|
|
1334
|
+ if ( pxebs_list_len ) {
|
|
1335
|
+ uint8_t buf[pxebs_list_len];
|
|
1336
|
+
|
|
1337
|
+ fetch_setting ( NULL, &pxe_boot_servers_setting,
|
|
1338
|
+ buf, sizeof ( buf ) );
|
|
1339
|
+ pxebs_list ( dhcp, buf, sizeof ( buf ), ip );
|
|
1340
|
+ }
|
|
1341
|
+ if ( ! dhcp->pxe_attempt->s_addr ) {
|
|
1342
|
+ DBGC ( dhcp, "DHCP %p has no PXE boot servers for type %04x\n",
|
|
1343
|
+ dhcp, pxe_type );
|
|
1344
|
+ rc = -EINVAL;
|
|
1345
|
+ goto err;
|
|
1346
|
+ }
|
|
1347
|
+
|
|
1348
|
+ /* Dump out PXE server lists */
|
|
1349
|
+ DBGC ( dhcp, "DHCP %p attempting", dhcp );
|
|
1350
|
+ for ( ip = dhcp->pxe_attempt ; ip->s_addr ; ip++ )
|
|
1351
|
+ DBGC ( dhcp, " %s", inet_ntoa ( *ip ) );
|
|
1352
|
+ DBGC ( dhcp, "\n" );
|
|
1353
|
+ if ( dhcp->pxe_accept ) {
|
|
1354
|
+ DBGC ( dhcp, "DHCP %p accepting", dhcp );
|
|
1355
|
+ for ( ip = dhcp->pxe_accept ; ip->s_addr ; ip++ )
|
|
1356
|
+ DBGC ( dhcp, " %s", inet_ntoa ( *ip ) );
|
|
1357
|
+ DBGC ( dhcp, "\n" );
|
|
1358
|
+ }
|
|
1359
|
+
|
1220
|
1360
|
/* Instantiate child objects and attach to our interfaces */
|
1221
|
1361
|
if ( ( rc = xfer_open_socket ( &dhcp->xfer, SOCK_DGRAM, &dhcp_peer,
|
1222
|
1362
|
( struct sockaddr * ) &dhcp->local ) ) != 0 )
|