diff --git a/src/include/ipxe/in.h b/src/include/ipxe/in.h index a1821b1f..a37784e2 100644 --- a/src/include/ipxe/in.h +++ b/src/include/ipxe/in.h @@ -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 */ diff --git a/src/net/ipv6.c b/src/net/ipv6.c index 69feba19..8dc251ba 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -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. */ @@ -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 ) { diff --git a/src/tests/ipv6_test.c b/src/tests/ipv6_test.c index 10e964d9..4de310ab 100644 --- a/src/tests/ipv6_test.c +++ b/src/tests/ipv6_test.c @@ -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 * @@ -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 */