ipv6 porting notes -- May 18th 2008, please report errors/issues to . This is _not_ an introduction to porting ipv4 applications to ipv6. This text isn't about APIs either, these are already explained elsewhere. Instead it is meant to document some caveats/pitfalls w.r.t. to different ipv6 stacks/implementations (and how to deal with these). 1) API Notes ============ In short: - Get address by host name: getaddrinfo(3). Caveats: not all ai_flags may be implemented on a platform; e.g. AI_ADDRCONFIG is present on Linux (glibc 2.6.1), but not defined in NetBSD 4.0. There are implementations (e.g. SunOS 5.10) that require (if a struct addrinfo is passed as a 'hints' parameter) that hints.ai_socktype is set (otherwise, EAI_SERVICE is returned). Solution: always set ai_socktype, even if you are not interested in the result sets ai_socktype. - Get host name from address (struct sockaddr): getnameinfo(3). - To convert a human-readable addresses to struct in_addr (AF_INET) or struct in6_addr (AF_INET6): inet_pton(3). - To convert struct in_addr/in6_addr: inet_ntop(3). Caveats: You need to explicitly tell both functions the address family, e.g. inet_pton(AF_INET6, ..) does _NOT_ convert an IPv4-dotted-decimal address; it wants a mapped address, e.g. ::FFFF:127.0.0.1. Furthermore, only getaddrinfo(3) is aware of IPv6 scoped addresses like fe80::1234%eth0. See RFC4007 for more information. (Thanks to Hagen Paul Pfeifer for pointing this out). Likewise, inet_ntop converts IPv6-mapped IPv4 addresses to IPv6 format. Therefore it might be better to avoid both inet_pton and inet_ntop and use getaddrinfo/gethostname for conversions, too. NI_NUMERICHOST can be specified in getnameinfo 'flags' parameter to disable DNS lookups. You can use AI_NUMERICHOST in getaddrinfo() hints.ai_flags to enforce a dotted-decimal address (or hexadecimal format for IPv6) as the node name. 2) Dual Stack Notes =================== Some operating systems (e.g. Linux) use a "dual stack" by default. This means that after bind()ing e.g. an IPPROTO_TCP AF_INET6 socket, bind()ing an AF_INET socket for the same source address will _fail_. accept() will also return connections initiated by IPv4 only clients. In this case, the source address is an IPv6-mapped IPv4 address (e.g. ::FFFF:192.168.0.1). You can use the IN6_IS_ADDR_V4MAPPED() macro to test if the specified struct in6_addr is an ipv4 address mapped to v6. Problem: some operating systems use a single stack by default, i.e. accept() will only return ipv6 connections. In this case, you can just bind() a second ipv4 socket using the same source address/port. Bottom line: try to bind ipv6 socket first If you are porting an ipv4 server app that can already deal with multiple simultaneous (ipv4) listening sockets, it might be better to just open two sockets (one ipv4, one ipv6) for application protocols like tcp. You can tell the Linux ipv6 stack that you only want ipv6 connections by using int on = 1; sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); [..] #ifdef IPV6_V6ONLY if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on))) err_handling_here(); #endif [..] bind(sock, ...) Caveat: Linux has a "SOL_IPV6" level. _DONT'T USE IT_; it's not portable. Use IPPROTO_IPV6 instead (both have the same value) 3) Misc. ======== You can use 'struct sockaddr_storage', which is large enough to hold 'struct sockaddr_in' or 'struct sockaddr_in6'. Caveat: 'struct sockaddr_storage' is usually larger than 'struct sockaddr_in6'. If you need to keep a lot of those addresses allocated, it might be better to use e.g. an anonymous union encapsulating sockaddr_in/_in6. Most people should just use sockaddr_storage. check netinet/in.h; there are a few macros that can be used to check if a given struct in6_addr is of a certain type, e.g. multicast or link local.