aboutsummaryrefslogtreecommitdiffstats
path: root/gpxe/src/net
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2008-09-07 22:41:29 -0700
committerH. Peter Anvin <hpa@zytor.com>2008-09-07 22:41:29 -0700
commitc14f98ab23dbc912aa9db26d86434a4d2bd80a5f (patch)
treea0b1d6fd48ad30f8af19a88d11a8f4e61cebf52f /gpxe/src/net
parent3506d7fb195922b04c941650b1512440bdcc89e4 (diff)
downloadsyslinux.git-c14f98ab23dbc912aa9db26d86434a4d2bd80a5f.tar.gz
syslinux.git-c14f98ab23dbc912aa9db26d86434a4d2bd80a5f.tar.xz
syslinux.git-c14f98ab23dbc912aa9db26d86434a4d2bd80a5f.zip
gPXE: update gPXE to current git
Update gPXE to current git. gpxe-for-syslinux e3ef2094cfa26f874c5f8dbd687eb311830efcf0 gpxe main tree 8223084afc206000312611a3fcfa30a28500d1a3
Diffstat (limited to 'gpxe/src/net')
-rw-r--r--gpxe/src/net/dhcppkt.c3
-rw-r--r--gpxe/src/net/ethernet.c40
-rw-r--r--gpxe/src/net/fakedhcp.c8
-rw-r--r--gpxe/src/net/netdevice.c28
-rw-r--r--gpxe/src/net/retry.c28
-rw-r--r--gpxe/src/net/tcp/ftp.c49
-rw-r--r--gpxe/src/net/tcp/iscsi.c311
-rw-r--r--gpxe/src/net/udp.c5
-rw-r--r--gpxe/src/net/udp/dhcp.c100
-rw-r--r--gpxe/src/net/udp/tftp.c25
10 files changed, 465 insertions, 132 deletions
diff --git a/gpxe/src/net/dhcppkt.c b/gpxe/src/net/dhcppkt.c
index 2537e254..c8bf215b 100644
--- a/gpxe/src/net/dhcppkt.c
+++ b/gpxe/src/net/dhcppkt.c
@@ -138,12 +138,15 @@ find_dhcp_packet_field ( unsigned int tag ) {
int dhcppkt_store ( struct dhcp_packet *dhcppkt, unsigned int tag,
const void *data, size_t len ) {
struct dhcp_packet_field *field;
+ void *field_data;
int rc;
/* If this is a special field, fill it in */
if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) {
if ( len > field->len )
return -ENOSPC;
+ field_data = dhcp_packet_field ( dhcppkt->dhcphdr, field );
+ memset ( field_data, 0, field->len );
memcpy ( dhcp_packet_field ( dhcppkt->dhcphdr, field ),
data, len );
return 0;
diff --git a/gpxe/src/net/ethernet.c b/gpxe/src/net/ethernet.c
index 55035de5..7b1c496f 100644
--- a/gpxe/src/net/ethernet.c
+++ b/gpxe/src/net/ethernet.c
@@ -38,17 +38,16 @@
static uint8_t eth_broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
/**
- * Transmit Ethernet packet
+ * Add Ethernet link-layer header
*
* @v iobuf I/O buffer
* @v netdev Network device
* @v net_protocol Network-layer protocol
* @v ll_dest Link-layer destination address
- *
- * Prepends the Ethernet link-layer header and transmits the packet.
*/
-static int eth_tx ( struct io_buffer *iobuf, struct net_device *netdev,
- struct net_protocol *net_protocol, const void *ll_dest ) {
+static int eth_push ( struct io_buffer *iobuf, struct net_device *netdev,
+ struct net_protocol *net_protocol,
+ const void *ll_dest ) {
struct ethhdr *ethhdr = iob_push ( iobuf, sizeof ( *ethhdr ) );
/* Build Ethernet header */
@@ -56,35 +55,38 @@ static int eth_tx ( struct io_buffer *iobuf, struct net_device *netdev,
memcpy ( ethhdr->h_source, netdev->ll_addr, ETH_ALEN );
ethhdr->h_protocol = net_protocol->net_proto;
- /* Hand off to network device */
- return netdev_tx ( netdev, iobuf );
+ return 0;
}
/**
- * Process received Ethernet packet
- *
- * @v iobuf I/O buffer
- * @v netdev Network device
+ * Remove Ethernet link-layer header
*
- * Strips off the Ethernet link-layer header and passes up to the
- * network-layer protocol.
+ * @v iobuf I/O buffer
+ * @v netdev Network device
+ * @v net_proto Network-layer protocol, in network-byte order
+ * @v ll_source Source link-layer address
+ * @ret rc Return status code
*/
-static int eth_rx ( struct io_buffer *iobuf, struct net_device *netdev ) {
+static int eth_pull ( struct io_buffer *iobuf,
+ struct net_device *netdev __unused,
+ uint16_t *net_proto, const void **ll_source ) {
struct ethhdr *ethhdr = iobuf->data;
/* Sanity check */
if ( iob_len ( iobuf ) < sizeof ( *ethhdr ) ) {
DBG ( "Ethernet packet too short (%zd bytes)\n",
iob_len ( iobuf ) );
- free_iob ( iobuf );
return -EINVAL;
}
/* Strip off Ethernet header */
iob_pull ( iobuf, sizeof ( *ethhdr ) );
- /* Hand off to network-layer protocol */
- return net_rx ( iobuf, netdev, ethhdr->h_protocol, ethhdr->h_source );
+ /* Fill in required fields */
+ *net_proto = ethhdr->h_protocol;
+ *ll_source = ethhdr->h_source;
+
+ return 0;
}
/**
@@ -110,7 +112,7 @@ struct ll_protocol ethernet_protocol __ll_protocol = {
.ll_addr_len = ETH_ALEN,
.ll_header_len = ETH_HLEN,
.ll_broadcast = eth_broadcast,
- .tx = eth_tx,
- .rx = eth_rx,
+ .push = eth_push,
+ .pull = eth_pull,
.ntoa = eth_ntoa,
};
diff --git a/gpxe/src/net/fakedhcp.c b/gpxe/src/net/fakedhcp.c
index a10e442b..60264756 100644
--- a/gpxe/src/net/fakedhcp.c
+++ b/gpxe/src/net/fakedhcp.c
@@ -181,11 +181,9 @@ int create_fakeproxydhcpack ( struct net_device *netdev,
/* Identify ProxyDHCP settings */
settings = find_settings ( PROXYDHCP_SETTINGS_NAME );
- /* No ProxyDHCP settings => return empty block */
- if ( ! settings ) {
- memset ( data, 0, max_len );
- return 0;
- }
+ /* No ProxyDHCP settings => use normal DHCPACK */
+ if ( ! settings )
+ return create_fakedhcpack ( netdev, data, max_len );
/* Create base DHCPACK packet */
if ( ( rc = dhcp_create_packet ( &dhcppkt, netdev, DHCPACK, NULL,
diff --git a/gpxe/src/net/netdevice.c b/gpxe/src/net/netdevice.c
index 6875b3ba..3721b334 100644
--- a/gpxe/src/net/netdevice.c
+++ b/gpxe/src/net/netdevice.c
@@ -439,6 +439,7 @@ struct net_device * find_netdev_by_location ( unsigned int bus_type,
*/
int net_tx ( struct io_buffer *iobuf, struct net_device *netdev,
struct net_protocol *net_protocol, const void *ll_dest ) {
+ int rc;
/* Force a poll on the netdevice to (potentially) clear any
* backed-up TX completions. This is needed on some network
@@ -447,7 +448,15 @@ int net_tx ( struct io_buffer *iobuf, struct net_device *netdev,
*/
netdev_poll ( netdev );
- return netdev->ll_protocol->tx ( iobuf, netdev, net_protocol, ll_dest );
+ /* Add link-layer header */
+ if ( ( rc = netdev->ll_protocol->push ( iobuf, netdev, net_protocol,
+ ll_dest ) ) != 0 ) {
+ free_iob ( iobuf );
+ return rc;
+ }
+
+ /* Transmit packet */
+ return netdev_tx ( netdev, iobuf );
}
/**
@@ -485,6 +494,10 @@ int net_rx ( struct io_buffer *iobuf, struct net_device *netdev,
static void net_step ( struct process *process __unused ) {
struct net_device *netdev;
struct io_buffer *iobuf;
+ struct ll_protocol *ll_protocol;
+ uint16_t net_proto;
+ const void *ll_source;
+ int rc;
/* Poll and process each network device */
list_for_each_entry ( netdev, &net_devices, list ) {
@@ -499,10 +512,21 @@ static void net_step ( struct process *process __unused ) {
* NIC faster than they arrive.
*/
if ( ( iobuf = netdev_rx_dequeue ( netdev ) ) ) {
+
DBGC ( netdev, "NETDEV %p processing %p (%p+%zx)\n",
netdev, iobuf, iobuf->data,
iob_len ( iobuf ) );
- netdev->ll_protocol->rx ( iobuf, netdev );
+
+ /* Remove link-layer header */
+ ll_protocol = netdev->ll_protocol;
+ if ( ( rc = ll_protocol->pull ( iobuf, netdev,
+ &net_proto,
+ &ll_source ) ) != 0 ) {
+ free_iob ( iobuf );
+ continue;
+ }
+
+ net_rx ( iobuf, netdev, net_proto, ll_source );
}
}
}
diff --git a/gpxe/src/net/retry.c b/gpxe/src/net/retry.c
index 3c934018..2a645c97 100644
--- a/gpxe/src/net/retry.c
+++ b/gpxe/src/net/retry.c
@@ -36,20 +36,11 @@
*
*/
-/** Default timeout value */
-#define MIN_TIMEOUT ( TICKS_PER_SEC / 4 )
-
-/** Limit after which the timeout will be deemed permanent */
-#define MAX_TIMEOUT ( 10 * TICKS_PER_SEC )
-
/* The theoretical minimum that the algorithm in stop_timer() can
* adjust the timeout back down to is seven ticks, so set the minimum
* timeout to at least that value for the sake of consistency.
*/
-#if MIN_TIMEOUT < 7
-#undef MIN_TIMEOUT
#define MIN_TIMEOUT 7
-#endif
/** List of running timers */
static LIST_HEAD ( timers );
@@ -67,8 +58,17 @@ void start_timer ( struct retry_timer *timer ) {
if ( ! timer_running ( timer ) )
list_add ( &timer->list, &timers );
timer->start = currticks();
- if ( timer->timeout < MIN_TIMEOUT )
- timer->timeout = MIN_TIMEOUT;
+
+ /* 0 means "use default timeout" */
+ if ( timer->min_timeout == 0 )
+ timer->min_timeout = DEFAULT_MIN_TIMEOUT;
+ /* We must never be less than MIN_TIMEOUT under any circumstances */
+ if ( timer->min_timeout < MIN_TIMEOUT )
+ timer->min_timeout = MIN_TIMEOUT;
+ /* Honor user-specified minimum timeout */
+ if ( timer->timeout < timer->min_timeout )
+ timer->timeout = timer->min_timeout;
+
DBG2 ( "Timer %p started at time %ld (expires at %ld)\n",
timer, timer->start, ( timer->start + timer->timeout ) );
}
@@ -150,8 +150,10 @@ static void timer_expired ( struct retry_timer *timer ) {
/* Back off the timeout value */
timer->timeout <<= 1;
- if ( ( fail = ( timer->timeout > MAX_TIMEOUT ) ) )
- timer->timeout = MAX_TIMEOUT;
+ if ( timer->max_timeout == 0 ) /* 0 means "use default timeout" */
+ timer->max_timeout = DEFAULT_MAX_TIMEOUT;
+ if ( ( fail = ( timer->timeout > timer->max_timeout ) ) )
+ timer->timeout = timer->max_timeout;
DBG ( "Timer %p timeout backed off to %ld\n",
timer, timer->timeout );
diff --git a/gpxe/src/net/tcp/ftp.c b/gpxe/src/net/tcp/ftp.c
index ffb2fbff..3b88f7b6 100644
--- a/gpxe/src/net/tcp/ftp.c
+++ b/gpxe/src/net/tcp/ftp.c
@@ -35,6 +35,7 @@ enum ftp_state {
FTP_TYPE,
FTP_PASV,
FTP_RETR,
+ FTP_WAIT,
FTP_QUIT,
FTP_DONE,
};
@@ -116,14 +117,15 @@ static void ftp_done ( struct ftp_request *ftp, int rc ) {
* snprintf() call.
*/
static const char * ftp_strings[] = {
- [FTP_CONNECT] = "",
+ [FTP_CONNECT] = NULL,
[FTP_USER] = "USER anonymous\r\n",
[FTP_PASS] = "PASS etherboot@etherboot.org\r\n",
[FTP_TYPE] = "TYPE I\r\n",
[FTP_PASV] = "PASV\r\n",
- [FTP_RETR] = "RETR %s\r\n",
+ [FTP_RETR] = "RETR %s\r\n",
+ [FTP_WAIT] = NULL,
[FTP_QUIT] = "QUIT\r\n",
- [FTP_DONE] = "",
+ [FTP_DONE] = NULL,
};
/**
@@ -170,6 +172,27 @@ static void ftp_parse_value ( char **text, uint8_t *value, size_t len ) {
}
/**
+ * Move to next state and send the appropriate FTP control string
+ *
+ * @v ftp FTP request
+ *
+ */
+static void ftp_next_state ( struct ftp_request *ftp ) {
+
+ /* Move to next state */
+ if ( ftp->state < FTP_DONE )
+ ftp->state++;
+
+ /* Send control string if needed */
+ if ( ftp_strings[ftp->state] != NULL ) {
+ DBGC ( ftp, "FTP %p sending ", ftp );
+ DBGC ( ftp, ftp_strings[ftp->state], ftp->uri->path );
+ xfer_printf ( &ftp->control, ftp_strings[ftp->state],
+ ftp->uri->path );
+ }
+}
+
+/**
* Handle an FTP control channel response
*
* @v ftp FTP request
@@ -198,6 +221,7 @@ static void ftp_reply ( struct ftp_request *ftp ) {
( ( status_major == '3' ) && ( ftp->state == FTP_USER ) ) ) ){
/* Flag protocol error and close connections */
ftp_done ( ftp, -EPROTO );
+ return;
}
/* Open passive connection when we get "PASV" response */
@@ -223,17 +247,9 @@ static void ftp_reply ( struct ftp_request *ftp ) {
}
}
- /* Move to next state */
- if ( ftp->state < FTP_DONE )
- ftp->state++;
-
- /* Send control string */
- if ( ftp->state < FTP_DONE ) {
- DBGC ( ftp, "FTP %p sending ", ftp );
- DBGC ( ftp, ftp_strings[ftp->state], ftp->uri->path );
- xfer_printf ( &ftp->control, ftp_strings[ftp->state],
- ftp->uri->path );
- }
+ /* Move to next state and send control string */
+ ftp_next_state ( ftp );
+
}
/**
@@ -331,8 +347,11 @@ static void ftp_data_closed ( struct xfer_interface *data, int rc ) {
ftp, strerror ( rc ) );
/* If there was an error, close control channel and record status */
- if ( rc )
+ if ( rc ) {
ftp_done ( ftp, rc );
+ } else {
+ ftp_next_state ( ftp );
+ }
}
/**
diff --git a/gpxe/src/net/tcp/iscsi.c b/gpxe/src/net/tcp/iscsi.c
index a12fca85..a67415b6 100644
--- a/gpxe/src/net/tcp/iscsi.c
+++ b/gpxe/src/net/tcp/iscsi.c
@@ -49,11 +49,17 @@ static char *iscsi_explicit_initiator_iqn;
/** Default iSCSI initiator name (constructed from hostname) */
static char *iscsi_default_initiator_iqn;
-/** iSCSI username */
-static char *iscsi_username;
+/** iSCSI initiator username */
+static char *iscsi_initiator_username;
-/** iSCSI password */
-static char *iscsi_password;
+/** iSCSI initiator password */
+static char *iscsi_initiator_password;
+
+/** iSCSI target username */
+static char *iscsi_target_username;
+
+/** iSCSI target password */
+static char *iscsi_target_password;
static void iscsi_start_tx ( struct iscsi_session *iscsi );
static void iscsi_start_login ( struct iscsi_session *iscsi );
@@ -81,8 +87,10 @@ static void iscsi_free ( struct refcnt *refcnt ) {
free ( iscsi->target_address );
free ( iscsi->target_iqn );
- free ( iscsi->username );
- free ( iscsi->password );
+ free ( iscsi->initiator_username );
+ free ( iscsi->initiator_password );
+ free ( iscsi->target_username );
+ free ( iscsi->target_password );
chap_finish ( &iscsi->chap );
iscsi_rx_buffered_data_done ( iscsi );
free ( iscsi );
@@ -144,6 +152,9 @@ static void iscsi_close_connection ( struct iscsi_session *iscsi, int rc ) {
/* Clear connection status */
iscsi->status = 0;
+ /* Deauthenticate target */
+ iscsi->target_auth_ok = 0;
+
/* Reset TX and RX state machines */
iscsi->tx_state = ISCSI_TX_IDLE;
iscsi->rx_state = ISCSI_RX_BHS;
@@ -213,11 +224,12 @@ static void iscsi_start_command ( struct iscsi_session *iscsi ) {
command->cmdsn = htonl ( iscsi->cmdsn );
command->expstatsn = htonl ( iscsi->statsn + 1 );
memcpy ( &command->cdb, &iscsi->command->cdb, sizeof ( command->cdb ));
- DBGC ( iscsi, "iSCSI %p start " SCSI_CDB_FORMAT " %s %#zx\n",
- iscsi, SCSI_CDB_DATA ( command->cdb ),
- ( iscsi->command->data_in ? "in" : "out" ),
- ( iscsi->command->data_in ?
- iscsi->command->data_in_len : iscsi->command->data_out_len ));
+ DBGC2 ( iscsi, "iSCSI %p start " SCSI_CDB_FORMAT " %s %#zx\n",
+ iscsi, SCSI_CDB_DATA ( command->cdb ),
+ ( iscsi->command->data_in ? "in" : "out" ),
+ ( iscsi->command->data_in ?
+ iscsi->command->data_in_len :
+ iscsi->command->data_out_len ) );
}
/**
@@ -450,17 +462,25 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
void *data, size_t len ) {
unsigned int used = 0;
unsigned int i;
+ const char *auth_method;
if ( iscsi->status & ISCSI_STATUS_STRINGS_SECURITY ) {
+ /* Default to allowing no authentication */
+ auth_method = "None";
+ /* If we have a credential to supply, permit CHAP */
+ if ( iscsi->initiator_username )
+ auth_method = "CHAP,None";
+ /* If we have a credential to check, force CHAP */
+ if ( iscsi->target_username )
+ auth_method = "CHAP";
used += ssnprintf ( data + used, len - used,
"InitiatorName=%s%c"
"TargetName=%s%c"
"SessionType=Normal%c"
- "AuthMethod=%sNone%c",
+ "AuthMethod=%s%c",
iscsi_initiator_iqn(), 0,
iscsi->target_iqn, 0, 0,
- ( ( iscsi->username && iscsi->password ) ?
- "CHAP," : "" ), 0 );
+ auth_method, 0 );
}
if ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_ALGORITHM ) {
@@ -468,9 +488,10 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
}
if ( ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_RESPONSE ) ) {
+ assert ( iscsi->initiator_username != NULL );
used += ssnprintf ( data + used, len - used,
"CHAP_N=%s%cCHAP_R=0x",
- iscsi->username, 0 );
+ iscsi->initiator_username, 0 );
for ( i = 0 ; i < iscsi->chap.response_len ; i++ ) {
used += ssnprintf ( data + used, len - used, "%02x",
iscsi->chap.response[i] );
@@ -478,6 +499,17 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
used += ssnprintf ( data + used, len - used, "%c", 0 );
}
+ if ( ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_CHALLENGE ) ) {
+ used += ssnprintf ( data + used, len - used,
+ "CHAP_I=%d%cCHAP_C=0x",
+ iscsi->chap_challenge[0], 0 );
+ for ( i = 1 ; i < sizeof ( iscsi->chap_challenge ) ; i++ ) {
+ used += ssnprintf ( data + used, len - used, "%02x",
+ iscsi->chap_challenge[i] );
+ }
+ used += ssnprintf ( data + used, len - used, "%c", 0 );
+ }
+
if ( iscsi->status & ISCSI_STATUS_STRINGS_OPERATIONAL ) {
used += ssnprintf ( data + used, len - used,
"HeaderDigest=None%c"
@@ -602,12 +634,17 @@ static int iscsi_handle_targetaddress_value ( struct iscsi_session *iscsi,
static int iscsi_handle_authmethod_value ( struct iscsi_session *iscsi,
const char *value ) {
+ /* Mark target as authenticated if no authentication required */
+ if ( ! iscsi->target_username )
+ iscsi->target_auth_ok = 1;
+
/* If server requests CHAP, send the CHAP_A string */
if ( strcmp ( value, "CHAP" ) == 0 ) {
DBGC ( iscsi, "iSCSI %p initiating CHAP authentication\n",
iscsi );
iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_ALGORITHM;
}
+
return 0;
}
@@ -620,7 +657,6 @@ static int iscsi_handle_authmethod_value ( struct iscsi_session *iscsi,
*/
static int iscsi_handle_chap_a_value ( struct iscsi_session *iscsi,
const char *value ) {
- int rc;
/* We only ever offer "5" (i.e. MD5) as an algorithm, so if
* the server responds with anything else it is a protocol
@@ -632,13 +668,6 @@ static int iscsi_handle_chap_a_value ( struct iscsi_session *iscsi,
return -EPROTO;
}
- /* Prepare for CHAP with MD5 */
- if ( ( rc = chap_init ( &iscsi->chap, &md5_algorithm ) ) != 0 ) {
- DBGC ( iscsi, "iSCSI %p could not initialise CHAP: %s\n",
- iscsi, strerror ( rc ) );
- return rc;
- }
-
return 0;
}
@@ -653,6 +682,7 @@ static int iscsi_handle_chap_i_value ( struct iscsi_session *iscsi,
const char *value ) {
unsigned int identifier;
char *endp;
+ int rc;
/* The CHAP identifier is an integer value */
identifier = strtoul ( value, &endp, 0 );
@@ -662,13 +692,21 @@ static int iscsi_handle_chap_i_value ( struct iscsi_session *iscsi,
return -EPROTO;
}
+ /* Prepare for CHAP with MD5 */
+ chap_finish ( &iscsi->chap );
+ if ( ( rc = chap_init ( &iscsi->chap, &md5_algorithm ) ) != 0 ) {
+ DBGC ( iscsi, "iSCSI %p could not initialise CHAP: %s\n",
+ iscsi, strerror ( rc ) );
+ return rc;
+ }
+
/* Identifier and secret are the first two components of the
* challenge.
*/
chap_set_identifier ( &iscsi->chap, identifier );
- if ( iscsi->password ) {
- chap_update ( &iscsi->chap, iscsi->password,
- strlen ( iscsi->password ) );
+ if ( iscsi->initiator_password ) {
+ chap_update ( &iscsi->chap, iscsi->initiator_password,
+ strlen ( iscsi->initiator_password ) );
}
return 0;
@@ -686,11 +724,13 @@ static int iscsi_handle_chap_c_value ( struct iscsi_session *iscsi,
char buf[3];
char *endp;
uint8_t byte;
+ unsigned int i;
/* Check and strip leading "0x" */
if ( ( value[0] != '0' ) || ( value[1] != 'x' ) ) {
DBGC ( iscsi, "iSCSI %p saw invalid CHAP challenge \"%s\"\n",
iscsi, value );
+ return -EPROTO;
}
value += 2;
@@ -712,6 +752,114 @@ static int iscsi_handle_chap_c_value ( struct iscsi_session *iscsi,
chap_respond ( &iscsi->chap );
iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_RESPONSE;
+ /* Send CHAP challenge, if applicable */
+ if ( iscsi->target_username ) {
+ iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_CHALLENGE;
+ /* Generate CHAP challenge data */
+ for ( i = 0 ; i < sizeof ( iscsi->chap_challenge ) ; i++ ) {
+ iscsi->chap_challenge[i] = random();
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Handle iSCSI CHAP_N text value
+ *
+ * @v iscsi iSCSI session
+ * @v value CHAP_N value
+ * @ret rc Return status code
+ */
+static int iscsi_handle_chap_n_value ( struct iscsi_session *iscsi,
+ const char *value ) {
+
+ /* The target username isn't actually involved at any point in
+ * the authentication process; it merely serves to identify
+ * which password the target is using to generate the CHAP
+ * response. We unnecessarily verify that the username is as
+ * expected, in order to provide mildly helpful diagnostics if
+ * the target is supplying the wrong username/password
+ * combination.
+ */
+ if ( iscsi->target_username &&
+ ( strcmp ( iscsi->target_username, value ) != 0 ) ) {
+ DBGC ( iscsi, "iSCSI %p target username \"%s\" incorrect "
+ "(wanted \"%s\")\n",
+ iscsi, value, iscsi->target_username );
+ return -EACCES;
+ }
+
+ return 0;
+}
+
+/**
+ * Handle iSCSI CHAP_R text value
+ *
+ * @v iscsi iSCSI session
+ * @v value CHAP_R value
+ * @ret rc Return status code
+ */
+static int iscsi_handle_chap_r_value ( struct iscsi_session *iscsi,
+ const char *value ) {
+ char buf[3];
+ char *endp;
+ uint8_t byte;
+ unsigned int i;
+ int rc;
+
+ /* Generate CHAP response for verification */
+ chap_finish ( &iscsi->chap );
+ if ( ( rc = chap_init ( &iscsi->chap, &md5_algorithm ) ) != 0 ) {
+ DBGC ( iscsi, "iSCSI %p could not initialise CHAP: %s\n",
+ iscsi, strerror ( rc ) );
+ return rc;
+ }
+ chap_set_identifier ( &iscsi->chap, iscsi->chap_challenge[0] );
+ if ( iscsi->target_password ) {
+ chap_update ( &iscsi->chap, iscsi->target_password,
+ strlen ( iscsi->target_password ) );
+ }
+ chap_update ( &iscsi->chap, &iscsi->chap_challenge[1],
+ ( sizeof ( iscsi->chap_challenge ) - 1 ) );
+ chap_respond ( &iscsi->chap );
+
+ /* Check and strip leading "0x" */
+ if ( ( value[0] != '0' ) || ( value[1] != 'x' ) ) {
+ DBGC ( iscsi, "iSCSI %p saw invalid CHAP response \"%s\"\n",
+ iscsi, value );
+ return -EPROTO;
+ }
+ value += 2;
+
+ /* Check CHAP response length */
+ if ( strlen ( value ) != ( 2 * iscsi->chap.response_len ) ) {
+ DBGC ( iscsi, "iSCSI %p invalid CHAP response length\n",
+ iscsi );
+ return -EPROTO;
+ }
+
+ /* Process response an octet at a time */
+ for ( i = 0 ; ( value[0] && value[1] ) ; value += 2, i++ ) {
+ memcpy ( buf, value, 2 );
+ buf[2] = 0;
+ byte = strtoul ( buf, &endp, 16 );
+ if ( *endp != '\0' ) {
+ DBGC ( iscsi, "iSCSI %p saw invalid CHAP response "
+ "byte \"%s\"\n", iscsi, buf );
+ return -EPROTO;
+ }
+ if ( byte != iscsi->chap.response[i] ) {
+ DBGC ( iscsi, "iSCSI %p saw incorrect CHAP "
+ "response\n", iscsi );
+ return -EACCES;
+ }
+ }
+ assert ( i == iscsi->chap.response_len );
+
+ /* Mark session as authenticated */
+ iscsi->target_auth_ok = 1;
+
return 0;
}
@@ -739,6 +887,8 @@ static struct iscsi_string_type iscsi_string_types[] = {
{ "CHAP_A=", iscsi_handle_chap_a_value },
{ "CHAP_I=", iscsi_handle_chap_i_value },
{ "CHAP_C=", iscsi_handle_chap_c_value },
+ { "CHAP_N=", iscsi_handle_chap_n_value },
+ { "CHAP_R=", iscsi_handle_chap_r_value },
{ NULL, NULL }
};
@@ -939,6 +1089,13 @@ static int iscsi_rx_login_response ( struct iscsi_session *iscsi,
return 0;
}
+ /* Check that target authentication was successful (if required) */
+ if ( ! iscsi->target_auth_ok ) {
+ DBGC ( iscsi, "iSCSI %p nefarious target tried to bypass "
+ "authentication\n", iscsi );
+ return -EPROTO;
+ }
+
/* Reset retry count */
iscsi->retry_count = 0;
@@ -1148,9 +1305,9 @@ static int iscsi_rx_bhs ( struct iscsi_session *iscsi, const void *data,
size_t len, size_t remaining __unused ) {
memcpy ( &iscsi->rx_bhs.bytes[iscsi->rx_offset], data, len );
if ( ( iscsi->rx_offset + len ) >= sizeof ( iscsi->rx_bhs ) ) {
- DBGC ( iscsi, "iSCSI %p received PDU opcode %#x len %#lx\n",
- iscsi, iscsi->rx_bhs.common.opcode,
- ISCSI_DATA_LEN ( iscsi->rx_bhs.common.lengths ) );
+ DBGC2 ( iscsi, "iSCSI %p received PDU opcode %#x len %#lx\n",
+ iscsi, iscsi->rx_bhs.common.opcode,
+ ISCSI_DATA_LEN ( iscsi->rx_bhs.common.lengths ) );
}
return 0;
}
@@ -1546,26 +1703,61 @@ static int iscsi_parse_root_path ( struct iscsi_session *iscsi,
* Set iSCSI authentication details
*
* @v iscsi iSCSI session
- * @v username Username, if any
- * @v password Password, if any
+ * @v initiator_username Initiator username, if any
+ * @v initiator_password Initiator password, if any
+ * @v target_username Target username, if any
+ * @v target_password Target password, if any
* @ret rc Return status code
*/
static int iscsi_set_auth ( struct iscsi_session *iscsi,
- const char *username, const char *password ) {
-
- if ( username ) {
- iscsi->username = strdup ( username );
- if ( ! iscsi->username )
+ const char *initiator_username,
+ const char *initiator_password,
+ const char *target_username,
+ const char *target_password ) {
+
+ /* Check for initiator or target credentials */
+ if ( initiator_username || initiator_password ||
+ target_username || target_password ) {
+
+ /* We must have at least an initiator username+password */
+ if ( ! ( initiator_username && initiator_password ) )
+ goto invalid_auth;
+
+ /* Store initiator credentials */
+ iscsi->initiator_username = strdup ( initiator_username );
+ if ( ! iscsi->initiator_username )
return -ENOMEM;
- }
-
- if ( password ) {
- iscsi->password = strdup ( password );
- if ( ! iscsi->password )
+ iscsi->initiator_password = strdup ( initiator_password );
+ if ( ! iscsi->initiator_password )
return -ENOMEM;
+
+ /* Check for target credentials */
+ if ( target_username || target_password ) {
+
+ /* We must have target username+password */
+ if ( ! ( target_username && target_password ) )
+ goto invalid_auth;
+
+ /* Store target credentials */
+ iscsi->target_username = strdup ( target_username );
+ if ( ! iscsi->target_username )
+ return -ENOMEM;
+ iscsi->target_password = strdup ( target_password );
+ if ( ! iscsi->target_password )
+ return -ENOMEM;
+ }
}
return 0;
+
+ invalid_auth:
+ DBGC ( iscsi, "iSCSI %p invalid credentials: initiator "
+ "%sname,%spw, target %sname,%spw\n", iscsi,
+ ( initiator_username ? "" : "no " ),
+ ( initiator_password ? "" : "no " ),
+ ( target_username ? "" : "no " ),
+ ( target_password ? "" : "no " ) );
+ return -EINVAL;
}
/**
@@ -1591,8 +1783,11 @@ int iscsi_attach ( struct scsi_device *scsi, const char *root_path ) {
if ( ( rc = iscsi_parse_root_path ( iscsi, root_path ) ) != 0 )
goto err;
/* Set fields not specified by root path */
- if ( ( rc = iscsi_set_auth ( iscsi, iscsi_username,
- iscsi_password ) ) != 0 )
+ if ( ( rc = iscsi_set_auth ( iscsi,
+ iscsi_initiator_username,
+ iscsi_initiator_password,
+ iscsi_target_username,
+ iscsi_target_password ) ) != 0 )
goto err;
/* Sanity checks */
@@ -1635,6 +1830,22 @@ struct setting initiator_iqn_setting __setting = {
.type = &setting_type_string,
};
+/** iSCSI reverse username setting */
+struct setting reverse_username_setting __setting = {
+ .name = "reverse-username",
+ .description = "Reverse user name",
+ .tag = DHCP_EB_REVERSE_USERNAME,
+ .type = &setting_type_string,
+};
+
+/** iSCSI reverse password setting */
+struct setting reverse_password_setting __setting = {
+ .name = "reverse-password",
+ .description = "Reverse password",
+ .tag = DHCP_EB_REVERSE_PASSWORD,
+ .type = &setting_type_string,
+};
+
/** An iSCSI string setting */
struct iscsi_string_setting {
/** Setting */
@@ -1654,12 +1865,22 @@ static struct iscsi_string_setting iscsi_string_settings[] = {
},
{
.setting = &username_setting,
- .string = &iscsi_username,
+ .string = &iscsi_initiator_username,
.prefix = "",
},
{
.setting = &password_setting,
- .string = &iscsi_password,
+ .string = &iscsi_initiator_password,
+ .prefix = "",
+ },
+ {
+ .setting = &reverse_username_setting,
+ .string = &iscsi_target_username,
+ .prefix = "",
+ },
+ {
+ .setting = &reverse_password_setting,
+ .string = &iscsi_target_password,
.prefix = "",
},
{
diff --git a/gpxe/src/net/udp.c b/gpxe/src/net/udp.c
index 8df76a44..407ea14d 100644
--- a/gpxe/src/net/udp.c
+++ b/gpxe/src/net/udp.c
@@ -55,11 +55,12 @@ struct tcpip_protocol udp_protocol;
*/
static int udp_bind ( struct udp_connection *udp ) {
struct udp_connection *existing;
- static uint16_t try_port = 1024;
+ static uint16_t try_port = 1023;
/* If no port specified, find the first available port */
if ( ! udp->local.st_port ) {
- for ( ; try_port ; try_port++ ) {
+ while ( try_port ) {
+ try_port++;
if ( try_port < 1024 )
continue;
udp->local.st_port = htons ( try_port );
diff --git a/gpxe/src/net/udp/dhcp.c b/gpxe/src/net/udp/dhcp.c
index 6ff8afe9..5fcd56ea 100644
--- a/gpxe/src/net/udp/dhcp.c
+++ b/gpxe/src/net/udp/dhcp.c
@@ -66,12 +66,12 @@ static const uint8_t dhcp_op[] = {
/** Raw option data for options common to all DHCP requests */
static uint8_t dhcp_request_options_data[] = {
DHCP_MAX_MESSAGE_SIZE, DHCP_WORD ( ETH_MAX_MTU ),
+ DHCP_CLIENT_ARCHITECTURE, DHCP_WORD ( 0 ),
+ DHCP_CLIENT_NDI, DHCP_OPTION ( 1 /* UNDI */ , 2, 1 /* v2.1 */ ),
DHCP_VENDOR_CLASS_ID,
DHCP_STRING ( 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':',
'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '0', ':',
'U', 'N', 'D', 'I', ':', '0', '0', '2', '0', '0', '1' ),
- DHCP_CLIENT_ARCHITECTURE, DHCP_WORD ( 0 ),
- DHCP_CLIENT_NDI, DHCP_OPTION ( 1 /* UNDI */ , 2, 1 /* v2.1 */ ),
DHCP_PARAMETER_REQUEST_LIST,
DHCP_OPTION ( DHCP_SUBNET_MASK, DHCP_ROUTERS, DHCP_DNS_SERVERS,
DHCP_LOG_SERVERS, DHCP_HOST_NAME, DHCP_DOMAIN_NAME,
@@ -685,6 +685,7 @@ static void dhcp_store_dhcpoffer ( struct dhcp_session *dhcp,
*/
static void dhcp_rx_dhcpoffer ( struct dhcp_session *dhcp,
struct dhcp_settings *dhcpoffer ) {
+ struct in_addr server_id;
char vci[9]; /* "PXEClient" */
int len;
uint8_t ignore_proxy = 0;
@@ -692,7 +693,8 @@ static void dhcp_rx_dhcpoffer ( struct dhcp_session *dhcp,
/* Check for presence of DHCP server ID */
if ( dhcppkt_fetch ( &dhcpoffer->dhcppkt, DHCP_SERVER_IDENTIFIER,
- NULL, 0 ) != sizeof ( struct in_addr ) ) {
+ &server_id, sizeof ( server_id ) )
+ != sizeof ( server_id ) ) {
DBGC ( dhcp, "DHCP %p received DHCPOFFER %p missing server "
"identifier\n", dhcp, dhcpoffer );
return;
@@ -700,8 +702,9 @@ static void dhcp_rx_dhcpoffer ( struct dhcp_session *dhcp,
/* If there is an IP address, it's a normal DHCPOFFER */
if ( dhcpoffer->dhcppkt.dhcphdr->yiaddr.s_addr != 0 ) {
- DBGC ( dhcp, "DHCP %p received DHCPOFFER %p has IP address\n",
- dhcp, dhcpoffer );
+ DBGC ( dhcp, "DHCP %p received DHCPOFFER %p from %s has IP "
+ "address\n",
+ dhcp, dhcpoffer, inet_ntoa ( server_id ) );
dhcp_store_dhcpoffer ( dhcp, dhcpoffer, &dhcp->dhcpoffer );
}
@@ -713,8 +716,9 @@ static void dhcp_rx_dhcpoffer ( struct dhcp_session *dhcp,
vci, sizeof ( vci ) );
if ( ( len >= ( int ) sizeof ( vci ) ) &&
( strncmp ( "PXEClient", vci, sizeof ( vci ) ) == 0 ) ) {
- DBGC ( dhcp, "DHCP %p received DHCPOFFER %p is a "
- "ProxyDHCPOFFER\n", dhcp, dhcpoffer );
+ DBGC ( dhcp, "DHCP %p received DHCPOFFER %p from %s is a "
+ "ProxyDHCPOFFER\n",
+ dhcp, dhcpoffer, inet_ntoa ( server_id ) );
dhcp_store_dhcpoffer ( dhcp, dhcpoffer,
&dhcp->proxydhcpoffer );
}
@@ -802,8 +806,8 @@ static void dhcp_rx_dhcpack ( struct dhcp_session *dhcp,
dhcppkt_fetch ( &dhcpack->dhcppkt, DHCP_SERVER_IDENTIFIER,
&ack_server_id, sizeof ( ack_server_id ) );
if ( offer_server_id.s_addr != ack_server_id.s_addr ) {
- DBGC ( dhcp, "DHCP %p ignoring DHCPACK with wrong server ID\n",
- dhcp );
+ DBGC ( dhcp, "DHCP %p ignoring DHCPACK with wrong server ID "
+ "%s\n", dhcp, inet_ntoa ( ack_server_id ) );
return;
}
@@ -814,6 +818,7 @@ static void dhcp_rx_dhcpack ( struct dhcp_session *dhcp,
/* If we have a ProxyDHCPOFFER, transition to PROXYDHCPREQUEST */
if ( dhcp->proxydhcpoffer ) {
+ dhcp->timer.min_timeout = 0;
dhcp_set_state ( dhcp, DHCP_STATE_PROXYREQUEST );
return;
}
@@ -830,8 +835,22 @@ static void dhcp_rx_dhcpack ( struct dhcp_session *dhcp,
*/
static void dhcp_rx_proxydhcpack ( struct dhcp_session *dhcp,
struct dhcp_settings *proxydhcpack ) {
+ struct in_addr offer_server_id = { 0 };
+ struct in_addr ack_server_id = { 0 };
int rc;
+ /* Verify server ID matches */
+ assert ( dhcp->proxydhcpoffer != NULL );
+ dhcppkt_fetch ( &dhcp->proxydhcpoffer->dhcppkt, DHCP_SERVER_IDENTIFIER,
+ &offer_server_id, sizeof ( offer_server_id ) );
+ dhcppkt_fetch ( &proxydhcpack->dhcppkt, DHCP_SERVER_IDENTIFIER,
+ &ack_server_id, sizeof ( ack_server_id ) );
+ if ( offer_server_id.s_addr != ack_server_id.s_addr ) {
+ DBGC ( dhcp, "DHCP %p ignoring ProxyDHCPACK with wrong server "
+ "ID %s\n", dhcp, inet_ntoa ( ack_server_id ) );
+ return;
+ }
+
/* Rename settings */
proxydhcpack->settings.name = PROXYDHCP_SETTINGS_NAME;
@@ -847,51 +866,76 @@ static void dhcp_rx_proxydhcpack ( struct dhcp_session *dhcp,
* Receive new data
*
* @v xfer Data transfer interface
- * @v data Received data
- * @v len Length of received data
+ * @v iobuf I/O buffer
+ * @v meta Transfer metadata
* @ret rc Return status code
*/
-static int dhcp_deliver_raw ( struct xfer_interface *xfer,
- const void *data, size_t len ) {
+static int dhcp_deliver_iob ( struct xfer_interface *xfer,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta ) {
struct dhcp_session *dhcp =
container_of ( xfer, struct dhcp_session, xfer );
+ struct sockaddr_tcpip *st_src;
+ unsigned int src_port;
struct dhcp_settings *dhcpset;
struct dhcphdr *dhcphdr;
uint8_t msgtype = 0;
+ int rc = 0;
+
+ /* Sanity checks */
+ if ( ! meta ) {
+ DBGC ( dhcp, "DHCP %p received packet without metadata\n",
+ dhcp );
+ rc = -EINVAL;
+ goto err_no_meta;
+ }
+ if ( ! meta->src ) {
+ DBGC ( dhcp, "DHCP %p received packet without source port\n",
+ dhcp );
+ rc = -EINVAL;
+ goto err_no_src;
+ }
+ st_src = ( struct sockaddr_tcpip * ) meta->src;
+ src_port = st_src->st_port;
/* Convert packet into a DHCP settings block */
- dhcpset = dhcpset_create ( data, len );
+ dhcpset = dhcpset_create ( iobuf->data, iob_len ( iobuf ) );
if ( ! dhcpset ) {
DBGC ( dhcp, "DHCP %p could not store DHCP packet\n", dhcp );
- return -ENOMEM;
+ rc = -ENOMEM;
+ goto err_dhcpset_create;
}
dhcphdr = dhcpset->dhcppkt.dhcphdr;
/* Identify message type */
dhcppkt_fetch ( &dhcpset->dhcppkt, DHCP_MESSAGE_TYPE, &msgtype,
sizeof ( msgtype ) );
- DBGC ( dhcp, "DHCP %p received %s %p\n",
- dhcp, dhcp_msgtype_name ( msgtype ), dhcpset );
+ DBGC ( dhcp, "DHCP %p received %s %p from port %d\n", dhcp,
+ dhcp_msgtype_name ( msgtype ), dhcpset, ntohs ( src_port ) );
/* Check for matching transaction ID */
if ( dhcphdr->xid != dhcp_xid ( dhcp->netdev ) ) {
DBGC ( dhcp, "DHCP %p received %s %p has bad transaction ID\n",
dhcp, dhcp_msgtype_name ( msgtype ), dhcpset );
- goto out;
+ rc = -EINVAL;
+ goto err_xid;
};
/* Handle packet based on current state */
switch ( dhcp->state ) {
case DHCP_STATE_DISCOVER:
- if ( msgtype == DHCPOFFER )
+ if ( ( msgtype == DHCPOFFER ) &&
+ ( src_port == htons ( BOOTPS_PORT ) ) )
dhcp_rx_dhcpoffer ( dhcp, dhcpset );
break;
case DHCP_STATE_REQUEST:
- if ( msgtype == DHCPACK )
+ if ( ( msgtype == DHCPACK ) &&
+ ( src_port == htons ( BOOTPS_PORT ) ) )
dhcp_rx_dhcpack ( dhcp, dhcpset );
break;
case DHCP_STATE_PROXYREQUEST:
- if ( msgtype == DHCPACK )
+ if ( ( msgtype == DHCPACK ) &&
+ ( src_port == htons ( PROXYDHCP_PORT ) ) )
dhcp_rx_proxydhcpack ( dhcp, dhcpset );
break;
default:
@@ -899,9 +943,13 @@ static int dhcp_deliver_raw ( struct xfer_interface *xfer,
break;
}
- out:
+ err_xid:
dhcpset_put ( dhcpset );
- return 0;
+ err_dhcpset_create:
+ err_no_src:
+ err_no_meta:
+ free_iob ( iobuf );
+ return rc;
}
/** DHCP data transfer interface operations */
@@ -910,8 +958,8 @@ static struct xfer_interface_operations dhcp_xfer_operations = {
.vredirect = xfer_vopen,
.window = unlimited_xfer_window,
.alloc_iob = default_xfer_alloc_iob,
- .deliver_iob = xfer_deliver_as_raw,
- .deliver_raw = dhcp_deliver_raw,
+ .deliver_iob = dhcp_deliver_iob,
+ .deliver_raw = xfer_deliver_as_iob,
};
/**
@@ -1012,6 +1060,8 @@ int start_dhcp ( struct job_interface *job, struct net_device *netdev ) {
xfer_init ( &dhcp->xfer, &dhcp_xfer_operations, &dhcp->refcnt );
dhcp->netdev = netdev_get ( netdev );
dhcp->timer.expired = dhcp_timer_expired;
+ dhcp->timer.min_timeout = DHCP_MIN_TIMEOUT;
+ dhcp->timer.max_timeout = DHCP_MAX_TIMEOUT;
dhcp->start = currticks();
/* Instantiate child objects and attach to our interfaces */
diff --git a/gpxe/src/net/udp/tftp.c b/gpxe/src/net/udp/tftp.c
index e49bcf9f..8fdb3714 100644
--- a/gpxe/src/net/udp/tftp.c
+++ b/gpxe/src/net/udp/tftp.c
@@ -316,17 +316,30 @@ void tftp_set_mtftp_port ( unsigned int port ) {
*/
static int tftp_send_rrq ( struct tftp_request *tftp ) {
struct tftp_rrq *rrq;
- const char *path = tftp->uri->path;
- size_t len = ( sizeof ( *rrq ) + strlen ( path ) + 1 /* NUL */
- + 5 + 1 /* "octet" + NUL */
- + 7 + 1 + 5 + 1 /* "blksize" + NUL + ddddd + NUL */
- + 5 + 1 + 1 + 1 /* "tsize" + NUL + "0" + NUL */
- + 9 + 1 + 1 /* "multicast" + NUL + NUL */ );
+ const char *path;
+ size_t len;
struct io_buffer *iobuf;
+ /* Strip initial '/' if present. If we were opened via the
+ * URI interface, then there will be an initial '/', since a
+ * full tftp:// URI provides no way to specify a non-absolute
+ * path. However, many TFTP servers (particularly Windows
+ * TFTP servers) complain about having an initial '/', and it
+ * violates user expectations to have a '/' silently added to
+ * the DHCP-specified filename.
+ */
+ path = tftp->uri->path;
+ if ( *path == '/' )
+ path++;
+
DBGC ( tftp, "TFTP %p requesting \"%s\"\n", tftp, path );
/* Allocate buffer */
+ len = ( sizeof ( *rrq ) + strlen ( path ) + 1 /* NUL */
+ + 5 + 1 /* "octet" + NUL */
+ + 7 + 1 + 5 + 1 /* "blksize" + NUL + ddddd + NUL */
+ + 5 + 1 + 1 + 1 /* "tsize" + NUL + "0" + NUL */
+ + 9 + 1 + 1 /* "multicast" + NUL + NUL */ );
iobuf = xfer_alloc_iob ( &tftp->socket, len );
if ( ! iobuf )
return -ENOMEM;