Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[ipv6] Add inet6_aton()
Signed-off-by: Michael Brown <mcb30@ipxe.org>
  • Loading branch information
mcb30 committed Sep 11, 2013
1 parent 8dd180f commit a9fa0d5
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 5 deletions.
3 changes: 2 additions & 1 deletion src/include/ipxe/in.h
Expand Up @@ -120,6 +120,7 @@ struct sockaddr_in6 {

extern int inet_aton ( const char *cp, struct in_addr *inp );
extern char * inet_ntoa ( struct in_addr in );
extern char * inet6_ntoa ( const struct in6_addr *in6 );
extern int inet6_aton ( const char *string, struct in6_addr *in );
extern char * inet6_ntoa ( const struct in6_addr *in );

#endif /* _IPXE_IN_H */
77 changes: 73 additions & 4 deletions src/net/ipv6.c
Expand Up @@ -622,11 +622,80 @@ static int ipv6_rx ( struct io_buffer *iobuf, struct net_device *netdev,
return rc;
}

/**
* Parse IPv6 address
*
* @v string IPv6 address string
* @ret in IPv6 address to fill in
* @ret rc Return status code
*/
int inet6_aton ( const char *string, struct in6_addr *in ) {
uint16_t *word = in->s6_addr16;
uint16_t *end = ( word + ( sizeof ( in->s6_addr16 ) /
sizeof ( in->s6_addr16[0] ) ) );
uint16_t *pad = NULL;
const char *nptr = string;
char *endptr;
unsigned long value;
size_t pad_len;
size_t move_len;

/* Parse string */
while ( 1 ) {

/* Parse current word */
value = strtoul ( nptr, &endptr, 16 );
if ( value > 0xffff ) {
DBG ( "IPv6 invalid word value %#lx in \"%s\"\n",
value, string );
return -EINVAL;
}
*(word++) = htons ( value );

/* Parse separator */
if ( ! *endptr )
break;
if ( *endptr != ':' ) {
DBG ( "IPv6 invalid separator '%c' in \"%s\"\n",
*endptr, string );
return -EINVAL;
}
if ( ( endptr == nptr ) && ( nptr != string ) ) {
if ( pad ) {
DBG ( "IPv6 invalid multiple \"::\" in "
"\"%s\"\n", string );
return -EINVAL;
}
pad = word;
}
nptr = ( endptr + 1 );

/* Check for overrun */
if ( word == end ) {
DBG ( "IPv6 too many words in \"%s\"\n", string );
return -EINVAL;
}
}

/* Insert padding if specified */
if ( pad ) {
move_len = ( ( ( void * ) word ) - ( ( void * ) pad ) );
pad_len = ( ( ( void * ) end ) - ( ( void * ) word ) );
memmove ( ( ( ( void * ) pad ) + pad_len ), pad, move_len );
memset ( pad, 0, pad_len );
} else if ( word != end ) {
DBG ( "IPv6 underlength address \"%s\"\n", string );
return -EINVAL;
}

return 0;
}

/**
* Convert IPv6 address to standard notation
*
* @v in IPv6 address
* @ret string IPv6 address in standard notation
* @v in IPv6 address
* @ret string IPv6 address string in canonical format
*
* RFC5952 defines the canonical format for IPv6 textual representation.
*/
Expand Down Expand Up @@ -672,8 +741,8 @@ char * inet6_ntoa ( const struct in6_addr *in ) {
/**
* Transcribe IPv6 address
*
* @v net_addr IPv6 address
* @ret string IPv6 address in standard notation
* @v net_addr IPv6 address
* @ret string IPv6 address in standard notation
*
*/
static const char * ipv6_ntoa ( const void *net_addr ) {
Expand Down
66 changes: 66 additions & 0 deletions src/tests/ipv6_test.c
Expand Up @@ -60,6 +60,37 @@ FILE_LICENCE ( GPL2_OR_LATER );
ok ( strcmp ( actual, expected ) == 0 ); \
} while ( 0 )

/**
* Report an inet6_aton() test result
*
* @v text Textual representation
* @v addr Expected IPv6 address
*/
#define inet6_aton_ok( text, addr ) do { \
static const char string[] = text; \
static const struct in6_addr expected = { \
.s6_addr = addr, \
}; \
struct in6_addr actual; \
\
ok ( inet6_aton ( string, &actual ) == 0 ); \
DBG ( "inet6_aton ( \"%s\" ) = %s\n", string, \
inet6_ntoa ( &actual ) ); \
ok ( memcmp ( &actual, &expected, sizeof ( actual ) ) == 0 ); \
} while ( 0 )

/**
* Report an inet6_aton() failure test result
*
* @v text Textual representation
*/
#define inet6_aton_fail_ok( text ) do { \
static const char string[] = text; \
struct in6_addr dummy; \
\
ok ( inet6_aton ( string, &dummy ) != 0 ); \
} while ( 0 )

/**
* Perform IPv6 self-tests
*
Expand Down Expand Up @@ -106,6 +137,41 @@ static void ipv6_test_exec ( void ) {
inet6_ntoa_ok ( IPV6 ( 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff ),
"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" );

/* inet6_aton() tests */
inet6_aton_ok ( "2001:ba8:0:1d4::6950:5845",
IPV6 ( 0x20, 0x01, 0x0b, 0xa8, 0x00, 0x00, 0x01, 0xd4,
0x00, 0x00, 0x00, 0x00, 0x69, 0x50, 0x58, 0x45));
/* No zeros */
inet6_aton_ok ( "2001:db8:1:1:1:1:1:1",
IPV6 ( 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x01,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01));
/* All intervening zeros */
inet6_aton_ok ( "fe80::1",
IPV6 ( 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01));
/* Trailing run of zeros */
inet6_aton_ok ( "fe80::",
IPV6 ( 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00));
/* Leading run of zeros */
inet6_aton_ok ( "::1",
IPV6 ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01));
/* All zeros */
inet6_aton_ok ( "::",
IPV6 ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00));

/* inet6_aton() failure tests */
inet6_aton_fail_ok ( "20012:ba8:0:1d4::6950:5845" );
inet6_aton_fail_ok ( "200z:ba8:0:1d4::6950:5845" );
inet6_aton_fail_ok ( "2001.ba8:0:1d4::6950:5845" );
inet6_aton_fail_ok ( "2001:db8:1:1:1:1:1" );
inet6_aton_fail_ok ( "2001:db8:1:1:1:1:1:1:2" );
inet6_aton_fail_ok ( "2001:db8::1::2" );
inet6_aton_fail_ok ( "2001:ba8:0:1d4:::6950:5845" );
inet6_aton_fail_ok ( ":::" );
}

/** IPv6 self-test */
Expand Down

0 comments on commit a9fa0d5

Please sign in to comment.