aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2013-09-11 04:26:39 +0100
committerMichael Brown <mcb30@ipxe.org>2013-09-11 11:27:13 +0100
commita9fa0d5f2bd5254464f63a7312857fcec83bf37f (patch)
tree53ff15f22819b38c29219ed1a76513040f07bd2d
parent8dd180f165eb8cd0a8475c0fd19bd6dbf17f95d8 (diff)
downloadipxe-a9fa0d5f2bd5254464f63a7312857fcec83bf37f.tar.gz
[ipv6] Add inet6_aton()
Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r--src/include/ipxe/in.h3
-rw-r--r--src/net/ipv6.c77
-rw-r--r--src/tests/ipv6_test.c66
3 files changed, 141 insertions, 5 deletions
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
@@ -623,10 +623,79 @@ static int ipv6_rx ( struct io_buffer *iobuf, struct net_device *netdev,
}
/**
+ * 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
@@ -61,6 +61,37 @@ FILE_LICENCE ( GPL2_OR_LATER );
} 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 */