제임스딘딘의
Tech & Life

개발자의 기록 노트/C

[네트워크/C reference] Special IPv6 주소 검사 매크로

제임스-딘딘 2017. 12. 14. 02:25

개요


IPv6 주소 체계에서는 'Special IPv6 addresses' 라고 부르는, 미리 정의된 특별한 주소들이 있다.

미리 정의된 비트 패턴(bit pattern) 으로 각 주소의 역할을 구분한다.


최근들어 C언어로 네트워크 프로그래밍을 하면서 IPv6 주소를 다루게 되는 경우가 발생하곤 하는데, 이 주소가 특정 Special IPv6 address 인지 아닌지를 확인 해야 하는 경우가 있었다. 

그래서 조금 찾아보니, 이런 경우 유용하게 사용 할 수 있는 매크로 함수가 C의 표준 라이브러리인 glibc 의 netinet/in.h 헤더에 정의가 되어 있었다.


이번 포스팅에서는 이들 매크로 함수들을 간단히 소개한다.


특수 IPv6 주소(Special IPv6 Address) 검사 매크로 함수

IEEE Std 1003.1, 2004 Edition 에는, <netinet/in.h> 헤더는 아래와 같은 매크로들을 정의해야 한다고 권고하고 있다.

이 각각의 매크로들은 인자로 하나의 const struct in6_addr * type을 받으며, 인자의 IPv6 주소가 special IPv6 addresses 인지 아닌지를 검사하고 int type으로 참인 경우 1 / 거짓인 경우 0을 반환하는 기능을 한다.


아래 내용중, 실제 정의 내용은 glibc/inet/netinet/in.h 에서 발췌했다. 정확한 버전은 기억나지 않는다. 

변경될 만한 성격의 것이 아니라 버전마다 다를 가능성은 적지만, 단언하긴 어려우며 확인이 필요하다.



IN6_IS_ADDR_UNSPECIFIED(addr)

정의되지 않은 주소(Unspecified address) 여부를 검사한다.

정의되지 않은 주소이면 1을 반환한다.

정의되지 않은 주소가 아니면 0을 반환한다.


참고로 IPv6에서 정의되지 않은 주소는 ::/128 이다.


헤더파일에는 아래와 같이 정의되어 있다.


#ifdef __GNUC__
# define IN6_IS_ADDR_UNSPECIFIED(a) \
  (__extension__                                                              \
   ({ const struct in6_addr *__a = (const struct in6_addr *) (a);              \
      __a->__in6_u.__u6_addr32[0] == 0                                              \
      && __a->__in6_u.__u6_addr32[1] == 0                                      \
      && __a->__in6_u.__u6_addr32[2] == 0                                      \
      && __a->__in6_u.__u6_addr32[3] == 0; }))
#else
# define IN6_IS_ADDR_UNSPECIFIED(a) \
        (((const uint32_t *) (a))[0] == 0                                      \
         && ((const uint32_t *) (a))[1] == 0                                      \
         && ((const uint32_t *) (a))[2] == 0                                      \
         && ((const uint32_t *) (a))[3] == 0)

#endif





IN6_IS_ADDR_LOOPBACK(addr)

local host로의 루프백 주소(Loopback address) 여부를 검사한다.

루프백 주소이면 1을 반환한다.

루프백 주소가 아니면 0을 반환한다.


참고로 IPv6에서 루프백 주소는 ::1/128 이다. IPv4의 127.0.0.1과 같은 역할을 한다.


헤더파일에는 아래와 같이 정의되어 있다.


#ifdef __GNUC__
# define IN6_IS_ADDR_LOOPBACK(a) \
  (__extension__                                                              \
   ({ const struct in6_addr *__a = (const struct in6_addr *) (a);              \
      __a->__in6_u.__u6_addr32[0] == 0                                              \
      && __a->__in6_u.__u6_addr32[1] == 0                                      \
      && __a->__in6_u.__u6_addr32[2] == 0                                      \
      && __a->__in6_u.__u6_addr32[3] == htonl (1); }))
#else
# define IN6_IS_ADDR_LOOPBACK(a) \
        (((const uint32_t *) (a))[0] == 0                                      \
         && ((const uint32_t *) (a))[1] == 0                                      \
         && ((const uint32_t *) (a))[2] == 0                                      \
         && ((const uint32_t *) (a))[3] == htonl (1))
#endif



IN6_IS_ADDR_MULTICAST(addr)

멀티캐스트 주소 (Multicast address) 여부를 검사한다.

멀티캐스트 주소이면 1을 반환한다.

멀티캐스트 주소가 아니면 0을 반환한다.


IPv6 에서 멀티캐스트 주소라고 하면, ff0X:: 로 시작하는 주소이다. 여기서 X는 16 진수 값으로 예약되어 있으므로 멀티캐스트 그룹에 할당하면 안된다. 이 예약에 대해서는 IANA (Internet Assigned Numbers Authority)가 관리한다. 현재까지는, 이 X의 값으로 scope를 구분한다.

현재는 0, 1, 2, 3, 5, 6, 7, 8, e, f 가 사용되고 있는데, 이중.. 1 (interface-local), 2 (link-local), 5 (site-local)를 의미한다. 다른 값들도 특정한 의미가 있다.


간단히 예를 들어보면, ff02::2 는 link-local의 모든 라우터를 지칭하는 주소이고, ff01::2 는 interface-local의 모든 라우터를 지칭하는 주소이며, ff05::2는 site-local의 모든 라우터를 지칭하는 주소인 식이다.

이에 대해서는 별도의 포스팅으로 한번 더 다뤄보겠다.


헤더파일에는 이 매크로를 아래와 같이 정의하고 있다.


#define IN6_IS_ADDR_MULTICAST(a) (((const uint8_t *) (a))[0] == 0xff)





IN6_IS_ADDR_LINKLOCAL(addr)

유니캐스트 링크로컬 주소(Unicast link-local address) 여부를 검사한다.

유니캐스트 링크로컬 주소이면  1을 반환한다.

유니캐스트 링크로컬 주소가 아니면 0을 반환한다.


헤더파일에는 아래와 같이 정의하고 있다.


#ifdef __GNUC__
# define IN6_IS_ADDR_LINKLOCAL(a) \
  (__extension__                                                              \
   ({ const struct in6_addr *__a = (const struct in6_addr *) (a);              \
      (__a->__in6_u.__u6_addr32[0] & htonl (0xffc00000)) == htonl (0xfe800000); }))
#else
# define IN6_IS_ADDR_LINKLOCAL(a) \
        ((((const uint32_t *) (a))[0] & htonl (0xffc00000))                      \
         == htonl (0xfe800000))
#endif




IN6_IS_ADDR_SITELOCAL(addr)

유니캐스트 사이트 로컬 주소(Unicast site-local address) 여부를 검사한다.

유니캐스트 사이트 로컬 주소이면  1을 반환한다.

유니캐스트 사이트 로컬 주소가 아니면 0을 반환한다.


헤더파일에는 아래와 같이 정의되어 있다.


#ifdef __GNUC__
# define IN6_IS_ADDR_SITELOCAL(a) \
  (__extension__                                                              \
   ({ const struct in6_addr *__a = (const struct in6_addr *) (a);              \
      (__a->__in6_u.__u6_addr32[0] & htonl (0xffc00000)) == htonl (0xfec00000); }))
#else
# define IN6_IS_ADDR_SITELOCAL(a) \
        ((((const uint32_t *) (a))[0] & htonl (0xffc00000))                      \
         == htonl (0xfec00000))
#endif




IN6_IS_ADDR_V4MAPPED(addr)

IPv4 매핑된 주소(IPv4 mapped address) 여부를 검사한다.

참고로 IPv4 mapped address는 prefix가 ::ffff:0:0/96 이고, 범위는 ::ffff:0.0.0.0부터 ::ffff:255.255.255.255 까지 이다.

이 매크로는, 인자 addr이 이에 해당하는 지를 검사하여 결과를 반환한다.


헤더파일에는 아래와 같이 정의되어 있다.


#ifdef __GNUC__
# define IN6_IS_ADDR_V4MAPPED(a) \
  (__extension__                                                              \
   ({ const struct in6_addr *__a = (const struct in6_addr *) (a);              \
      __a->__in6_u.__u6_addr32[0] == 0                                              \
      && __a->__in6_u.__u6_addr32[1] == 0                                      \
      && __a->__in6_u.__u6_addr32[2] == htonl (0xffff); }))
#else
# define IN6_IS_ADDR_V4MAPPED(a) \
        ((((const uint32_t *) (a))[0] == 0)                                      \
         && (((const uint32_t *) (a))[1] == 0)                                      \
         && (((const uint32_t *) (a))[2] == htonl (0xffff)))
#endif


IN6_IS_ADDR_V4COMPAT(addr)

IPv4 호환 주소(IPv4-compatible address) 여부를 검사한다.

참고로 IPv4-compatible address의 prefix는 ::/96 이다. 그러나 이 주소 prefix는 deprecated 되었음을 기억하자.


이 매크로는 헤더파일에 아래와 같이 정의되어 있다.


#ifdef __GNUC__
# define IN6_IS_ADDR_V4COMPAT(a) \
  (__extension__                                                              \
   ({ const struct in6_addr *__a = (const struct in6_addr *) (a);              \
      __a->__in6_u.__u6_addr32[0] == 0                                              \
      && __a->__in6_u.__u6_addr32[1] == 0                                      \
      && __a->__in6_u.__u6_addr32[2] == 0                                      \
      && ntohl (__a->__in6_u.__u6_addr32[3]) > 1; }))
#else
# define IN6_IS_ADDR_V4COMPAT(a) \
        ((((const uint32_t *) (a))[0] == 0)                                      \
         && (((const uint32_t *) (a))[1] == 0)                                      \
         && (((const uint32_t *) (a))[2] == 0)                                      \
         && (ntohl (((const uint32_t *) (a))[3]) > 1))
#endif



IN6_IS_ADDR_MC_NODELOCAL

멀티캐스트 노드 로컬 주소 (Multicast node-local address) 여부를 검사한다.


이 매크로는 헤더파일에 아래와 같이 정의되어 있다.


#define IN6_IS_ADDR_MC_NODELOCAL(a) \
        (IN6_IS_ADDR_MULTICAST(a)                                              \
         && ((((const uint8_t *) (a))[1] & 0xf) == 0x1))




IN6_IS_ADDR_MC_LINKLOCAL


멀티캐스트 링크 로컬 주소 (Multicast link-local address) 여부를 검사한다.


이 매크로는 헤더파일에 아래와 같이 정의되어 있다.


#define IN6_IS_ADDR_MC_LINKLOCAL(a) \
        (IN6_IS_ADDR_MULTICAST(a)                                              \
         && ((((const uint8_t *) (a))[1] & 0xf) == 0x2))




IN6_IS_ADDR_MC_SITELOCAL

멀티캐스트 사이트-로컬 주소(Multicast site-local address) 여부를 검사한다.


멀티캐스트 사이트-로컬 주소이면 1을 반환한다.

멀티캐스트 사이트-로컬 주소가 아니면 0을 반환한다.


헤더파일에는 아래와 같이 정의되어 있다.


#define IN6_IS_ADDR_MC_SITELOCAL(a) \
        (IN6_IS_ADDR_MULTICAST(a)                                              \
         && ((((const uint8_t *) (a))[1] & 0xf) == 0x5))



IN6_IS_ADDR_MC_ORGLOCAL

멀티캐스트 organization-local 주소(Multicast organization-local address) 여부를 검사한다.


참고로 organization-local 주소의 prefix는 ffx8::/16 이고, IPv4의 239.192.0.0/14 와 동일하다.

헤더파일에는 아래와 같이 정의되어 있다.


#define IN6_IS_ADDR_MC_ORGLOCAL(a) \
        (IN6_IS_ADDR_MULTICAST(a)                                              \
         && ((((const uint8_t *) (a))[1] & 0xf) == 0x8))



IN6_IS_ADDR_MC_GLOBAL

멀티캐스트 글로벌 주소(Multicast global address) 여부를 검사한다. 

멀티캐스트 글로벌 주소이면 1을 반환한다.

멀티캐스트 글로벌 주소가 아니면 0을 반환한다.


헤더파일에는 아래와 같이 정의되어 있다.



#define IN6_IS_ADDR_MC_GLOBAL(a) \
        (IN6_IS_ADDR_MULTICAST(a)                                              \
         && ((((const uint8_t *) (a))[1] & 0xf) == 0xe))



참조 - 예약된 IPv6 주소(Reserved IPv6 address)

Special IPv6 address와 같이 볼 것이 있는데, Reserved IPv6 address 이다. IPv6 주소 체계에서 특수 목적으로 예약된 몇몇 주소들인데, 아래 wiki의 표와 같다.


https://en.wikipedia.org/wiki/Reserved_IP_addresses#IPv6