IPv6

  • 작성자
    조재혁 (Mminzkn@minzkn.com)

  • 고친과정
    2008년 6월 24일 : 처음씀
    2019년 5월 8일 : 최신 표준안을 기준으로 전체 내용 갱신


개요


본 문서에서는 최대한 IPv6 전반에 대한 표준안을 기준으로 종합적인 사항을 정리하는 것을 목표를 두고 작성되었습니다.

현재 작성 시점에서 최대한 [https]RFC4291[]을 기준으로 작성되었음을 밝힙니다. 당연히 본 문서보다 해당 표준을 보시는게 더 권장할만 합니다. 그리고 [https]RFC5952[]도 함께 보시는 것을 권장합니다.

그리고 실습환경으로 IPv6 공인망 구축이 어려우시다면 [https]IPv6 Tunnel Broker 구축 메뉴얼[]을 통해서 IPv6 환경을 먼저 준비하시고 본 문서를 읽으시면서 실습하신다면 보다 쉽게 이해가 가능할 것으로 예상됩니다.

IPv6 (Internet Protocol v6) header


IPv6 Header 구조
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 Description
4bit Version 8bit Traffic class 20bit Flow labal ↑↓8 bytes ↑↓40 bytes(IPv6 header size)
16-bit Payload length 8-bit Next header 8bit Hop limit
128bit Source address ↑↓32 bytes
128bit Destination address
nexthdr hdrlen extension-header ↑↓IPv6 extension header size ↑↓Variable size
~
nexthdr hdrlen extension-header
~
extension-header ...
Upper layer (ICMP/UDP/TCP/SCTP/AH/ESP/IPCOMP/...) ↑↓Upper layer protocol

  • Version : IP version을 나타내며 IPv6의 경우 6
  • Traffic class : Previously the type-of-service (ToS) field in IPv4, the traffic class field defines the class-of-service (CoS) priority of the packet. However, the semantics for this field (for example, DiffServ code points) are identical to IPv4.
  • Flow label : The flow label identifies all packets belonging to a specific flow (that is, packet flows requiring a specific class of service CoS); routers can identify these packets and handle them in a similar fashion.
  • Payload length : IPv6 payload의 크기를 나타냅니다. (즉, IPv6 기본 헤더를 제외한 IPv6 extension header 및 그 뒤에 오는 payload를 합산 크기)
  • Next header : IPv6 header 바로 다음에 오는 프로토콜의 종류를 나타내는 번호를 나타냅니다.
  • Hop limit : IPv4에서의 TTL(Time-To-Live)와 같은 개념으로 Hop 제한 개수를 나타냅니다.
  • Source address : 출발지 주소
  • Destination address : 목적지 주소
  • extension-header : 확장헤더로써 필요에 의해서 nexthdr의 종류에 따라서 여러개를 덧붙여서 표현하며 사용되는 nexthdr 로는 NEXTHDR_HOP(0), NEXTHDR_ROUTING(43), NEXTHDR_FRAGMENT(44), NEXTHDR_AUTH(51), NEXTHDR_NONE(59), NEXTHDR_DEST(60) 등이 있습니다.
  • Upper layer : IPv6 header 와 IPv6 extension header 의 다음에는 상위계층의 프로토콜이 위치합니다.

    IPv4 Header에서 IPv6 Header로의 변화에 있어서 Header는 다음과 같은 특징을 요약 할 수 있습니다.
    • IP Header Length 필드 제거
    • ToS(Type of Service) 필드는 Traffic class 필드로 대체
    • 전체 길이(Total Length) 필드는 Payload length 필드로 대체
    • 식별(Identification), Flags, Fragment offset 필드 제거
    • TTL(Time To Live) 필드는 Hop limit 이라고 명명
    • Protocol 필드는 Next header 필드로 대체
    • Option 필드는 확장 헤더(Extension header)로 구현

주소표현 방법

  • IPv6주소를 문자열로 표현하는 방법

    1. x:x:x:x:x:x:x:x형태로 표현되며 x는 16비트의 16진수로 표현합니다. 대소문자를 구분하지 않으며 각 항목의 상위 숫자 0은 생략해서 표기해도 되지만 각 항목에는 적어도 하나의 숫자가 있어야 합니다.

      예1) fe80:0000:0000:0000:0213:d4ff:fe47:14d5 예2) fe80:0:0:0:213:d4ff:fe47:14d5

    2. 일반적으로 0비트가 많이 포함되므로 이를 간략히 표기할 방법을 나타내기 위해서 16비트 0의 연속된 항목을 "::" 기호로 대체할수 있습니다. 단, 이러한 축약기호는 단 한번만 사용할수 있습니다.

      예1) fe80:0000:0000:0000:0213:d4ff:fe47:14d5 => fe80::213:d4ff:fe47:14d5 예2) 0:0:0:0:0:0:0:1 => ::1 예3) 0:0:0:0:0:0:0:0 => ::

    3. IPv4주소를 (포함하는) IPv6주소로 나타내는 경우 0:0:0:0:0:x:d.d.d.d 로 나타낼수 있습니다. 여기서 x는 16비트 16진수(0 또는 0xffff)이며 d는 8비트 10진수로 표현합니다.

      IPv6-Compatible IPv6 address
      예1) 0:0:0:0:0:0:192.168.0.1 => ::192.168.0.1

      IPv4-Mapped IPv6 Address
      예2) 0:0:0:0:0:ffff:192.168.0.2 => ::ffff:192.168.0.2

  • 프리픽스(Prefix)주소 표현하는 방법
    "IPv6주소/프리픽스길이" 로 표현하며 IPv4의 CIDR표현과 매우 유사한 방법으로 인접한 IP대역을 그룹화 하여 가르킬때 사용합니다. 프리픽스 표현에서 선행하는 0은 축약하여 표현할수 있지만 후행 0은 생략해서는 안됩니다.

    예1) fe80:0000:0000:0000:0213:d4ff:fe47:14d5/64 => fe80::215:17ff:fe26:c3ed/64

  • 범주지정 주소 표현하는 방법

    주소만으로는 어떤 범주인지를 명시하지 않았을 경우 오는 모호성을 해결할수 없는 경우가 있습니다. 이때 주소%영역 과 같이 표시하여 모호성을 해결합니다.

    예1) fe80::213:d4ff:fe47:14d5%eth0

  • C언어에서 관련 macro 또는 함수
    • INET_ADDRSTRLEN : IPv4 주소문자열의 최대 길이 ("111.111.111.111", 15 + 1)
    • INET6_ADDRSTRLEN : IPv6 주소문자열의 최대 길이 ("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255", 45 + 1)
    • IN6_ARE_ADDR_EQUAL(a,b) : 두개의 IPv6 주소 비교
    • int inet_pton(int af, const char *src, void *dst) : IPv4IPv6 주소 문자열을 struct in_addr 및 struct in6_addr 구조체로 변경 (문자열 주소가 유효성 검사에 만족하지 않으면 -1 반환)
    • const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) : IPv4IPv6 주소가 저장된 struct in_addr 및 struct in6_addr 구조체를 문자열로 변경

프리픽스 유형

IPv6 주소 Scope
주소유형 프리픽스 관련 C macro 또는 전역상수 비고(참고)
미지정(Unspecified) ::/128 IN6_IS_ADDR_UNSPECIFIED(a), IN6ADDR_ANY_INIT, struct in6_addr in6addr_any [https]RFC4291[]
루프백(Lookback) ::1/128 IN6_IS_ADDR_LOOPBACK(a), IN6ADDR_LOOPBACK_INIT, struct in6_addr in6addr_loopback [https]RFC4291[]
IPv4 호환 IPv6 주소(IPv4-compatible IPv6 address) ::<IPv4-address>/96, Ex) ::123.123.123.123 IN6_IS_ADDR_V4COMPAT(a) deprecated by [https]RFC4291[]
IPv4 맵핑 IPv6 주소(IPv4-mapped IPv6 address) ::ffff:<IPv4-address>/96, Ex) ::ffff:123.123.123.123 IN6_IS_ADDR_V4MAPPED(a) [https]RFC4291[]
Well-Known Prefix 64:ff9b::/96 [https]RFC6052[]
로컬 유니캐스트(Local unicast) fc00::/7 [https]RFC4193[]
멀티캐스트(Multicast) ff00::/8 IN6_IS_ADDR_MULTICAST(a), IN6_IS_ADDR_MC_NODELOCAL(a), IN6_IS_ADDR_MC_LINKLOCAL(a), IN6_IS_ADDR_MC_SITELOCAL(a), IN6_IS_ADDR_MC_ORGLOCAL(a), IN6_IS_ADDR_MC_GLOBAL(a) [https]RFC4291[]
링크로컬 유니캐스트(Link-Local unicast) fe80::/10 IN6_IS_ADDR_LINKLOCAL(a) [https]RFC4291[]
사이트로컬 유니캐스트(Site-Local unicast) fec0::/10 IN6_IS_ADDR_SITELOCAL(a) [https]RFC4291[], [https]RFC3879 - Deprecating Site Local Addresses[]
문서에만 사용할 목적의 전역 유니캐스트(Global unicast for document only) 2001:db8::/32 [https]RFC3849[]
전역 유니캐스트(Global unicast) 위의 유형을 제외한 나머지 모든 비트 [https]RFC4291[]

미지정(Unspecified) 주소

주소 ::는 미지정 주소라고 하는데 이 주소는 할당되어서는 안되는 주소이며 자신의 주소가 없는 상태일때 임의로 패킷을 발송하기 위해서 사용할수 있는 경우가 있습니다. 이 주소는 결코 IPv6 라우터에 의해서 포워딩 되어서는 안됩니다.

루프백(Loopback) 주소

주소 ::1는 루프백 주소로 자기자신과 통신할때 사용합니다. 이 주소는 어떠한 물리적 인터페이스장치에도 할당할수 없으며 가상 loopback 인터페이스에 할당됩니다. 목적지가 loopback 주소인 경우 외부로의 전송이 이루어질수 없으며 IPv6 라우터에 의해서 포워딩되지 않습니다.

IPv4주소를 포함하는 IPv6주소

IPv4주소를 포함하는 IPv6주소의 유형은 두가지가 존재합니다.

  1. IPv6-Compatible IPv6 address: IPv4 망내에서 IPv6 패킷을 터널링하기위한 기술에 사용되는 IPv4 호환 IPv6주소 (deprecated by [https]RFC4291[])
    0:0:0:0:0:0000:IPv4주소

  2. IPv4-Mapped IPv6 Address: 전역 IPv4주소를 IPv6로 표현하기 위한 IPv4 맵드 IPv6주소
    0:0:0:0:0:ffff:IPv4주소

로컬 유니캐스트 주소(Local unicast address)

로컬 유니캐스트 주소는 링크(Link)와 사이트(Site) 두가지의 유형이 존재합니다.

  1. 로컬 유니캐스트 주소 (Local unicast address)
    로컬 유니캐스트 주소 (Local unicast address)
    7bits 1bit 40bits 16bits 64bits
    1111 110 (fc00::/7) L Global ID Subnet ID Interface ID
    • Prefix: "FC00::/7"은 Local unicast address 임을 의미
      • (IPv6-address & FE00::) == FC00::
    • L: 1인 경우 지역내의 사용을 목적으로 할당된 경우를 의미, 0인 경우는 [https]RFC4193 Section-3.2[]을 참고
    • Global ID: 전역 식별자로 사용 ([https]RFC4193 Section-3.2[] 참고)
    • Subnet ID: 서브넷
    • Interface ID: 인터페이스 식별부분

  2. 링크 로컬 주소 (Link-local unicast address)

    인접탐색과 자동주소와 같은 목적을 하기위해서 단일링크상에 라우터가 없는 경우를 위해서 사용됩니다. 라우터는 이 주소를 출발지 또는 목적지로 갖는 패킷을 다른 링크로 포워딩 하지 않아야 합니다.
    링크 로컬 주소 (Link-local unicast address)
    10bits 54bits 64bits
    1111 1110 10 (fe80::/10) 0 Interface ID
    동일 링크(구역) 내에서 구별가능한 고유한 ID (Interface ID)를 생성하는 방법에는 크게 4가지를 생각해볼 수 있습니다.
    1. 수동 구성 (Manually Configured: 직접 구역내 고유한 ID를 관리하여 설정)
    2. 수정 EUI-64로 생성하여 관리 (Modified EUI-64)
      1. MAC 주소(EUI-48) 로부터 EUI-64로 변환 (이 때 중간에 FF FE 를 채워 넣습니다.)
      2. "u" (universal/local) bit (상위 7번째 bit) 를 반전(또는 MAC인 경우는 universal 하므로 1로 set) 시킵니다.
      3. 예) 만약 내 PC의 인터페이스 eth0의 MAC 주소가 "6C:62:6D:A9:7E:D0" 인 경우 "fe80::6e62:6dff:fea9:7ed0/64"가 assign 될 수 있는 주소가 됩니다.
        1. MAC 주소는 48bits(6bytes)를 해당 인터페이스 eth0로부터 확인합니다.
          48bits MAC 주소 "6C:62:6D:A9:7E:D0"
          MAC Address (EUI-48)
          OUI (Organizationally Unique Identifier) NIC(Network Interface Controller) Specific
          0110 1100 0110 0010 0110 1101 1010 1001 0111 1110 1101 0000
          (6C) (62) (6D) (A9) (7E) (D0)
        2. 해당 MAC 주소를 상위 24 bits(3 bytes)와 하위 24 bits(3 bytes) 로 분리하고 그 사이에 FF FE 값 16 bits 를 추가합니다.
          48bits MAC 주소를 상위 24bits와 하위 24bits로 분리하고 사이에 16bits인 FF FE 추가
          상위 24bits Mark 16bits (FF FE) 하위 24bits
          OUI (Organizationally Unique Identifier) NIC(Network Interface Controller) Specific
          0110 1100 0110 0010 0110 1101 1111 1111 1111 1110 1010 1001 0111 1110 1101 0000
          (6C) (62) (6D) (FF) (FE) (A9) (7E) (D0)
        3. 상위(MSB)에서 7번째 bit를 1로 set 합니다. (이 bit를 universal/local bit 라고 합니다. => 참고: [https]RFC4291 IP Version 6 Addressing Architecture[])
          상위(MSB)에서 7번째 bit(universal/local bit)를 1로 set
          EUI-64
          상위 24bits Mark 16bits (FF FE) 하위 24bits
          상위(MSB) 7번째 bit set
          0110 1110 0110 0010 0110 1101 1111 1111 1111 1110 1010 1001 0111 1110 1101 0000
          (6E) (62) (6D) (FF) (FE) (A9) (7E) (D0)
        4. 이제 IPv6 주소로 상위 10bit를 fe80::/10 으로 하고 하위 64bits를 위에서 계산한 Modified EUI-64 값으로 채워 넣으므로써 링크로컬 유일한 자동주소값이 만들어집니다. (최종 링크로컬 주소의 prefix는 64입니다.)
          생성된 최종 Assign될 주소 "fe80::6e62:6dff:fea9:7ed0/64"
          IPv6 Link-local address by EUI-64
          Prefix (fe80::/10) 0 상위 24bits Mark 16bits (FF FE) 하위 24bits
          1111 1110 1000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0110 1110 0110 0010 0110 1101 1111 1111 1111 1110 1010 1001 0111 1110 1101 0000
          (FE) (80) (00) (00) (00) (00) (00) (00) (6E) (62) (6D) (FF) (FE) (A9) (7E) (D0)
    3. DHCPv6 서버에 의해서 관리 (DHCPv6 server)
    4. 임의의 수를 생성하여 관리 (Auto-generated pseudo random number)

  3. 사이트 로컬 주소 (Site-local unicast address)

    전역 프리픽스 없이 사이트 내에서 통신하기 위해서 사용합니다. IPv4망에서의 사설망과 흡사하다고 보면 될듯 합니다.
    사이트 로컬 주소 (Site-local unicast address)
    10bits 54bits 64bits
    1111 1110 11 (fec0::/10) Subnet ID Interface ID

애니캐스트(Anycast) 주소

애니캐스트는 여러 주소유형중에서 하나를 사용하여 유니캐스트 주소공간으로부터 할당됩니다. 때문에 애니캐스트 주소는 유니캐스트 주소와 식별이 안됩니다.

NOTE: 필자는 현재 애니캐스트를 구체적으로 머리속에 그리지 못하고 있습니다. 여러가지 제약적 조건이 발생될수 있는 사항에 대해서도 복잡할뿐. 좀더 공부후에 정리계획입니다.

멀티캐스트(Multicast) 주소

멀티캐스트 주소는 다음과 같은 포맷으로 구성됩니다. ([https]RFC3306[])
멀티캐스트 주소 유형 #1
8bits 4bits 4bits 112bits
1111 1111 flags scope Group ID

멀티캐스트 주소는 다음과 같은 새로운 포맷으로 구성됩니다. ([https]RFC3306[])
멀티캐스트 주소 유형 #2
8bits 4bits 4bits 8bits 8bits 64bits 32bits
1111 1111 flags scope reserved(MUST be zero) plen network prefix group ID

flags의 4개 bits 는 다음과 같이 구성됩니다.
Multicast address flags
0(항상 zero) R([https]RFC3956[]) P([https]RFC3306[]) T([https]RFC4291[]
T가 0인 경우 IANA가 영구적으로 할당한 멀티캐스트 주소임을 표시합니다. 1인 경우는 그 외의 멀티캐스트 주소를 의미합니다.

scope는 다음과 같이 범주를 제한하는 용도로 사용됩니다.
Scope 값
설명 비고(참고)
0 예약 (reserved) [https]RFC4291[], [https]RFC7346[]
1 로컬 범위의 인터페이스 (interface-local scope) [https]RFC4291[], [https]RFC7346[]
2 로컬 범위의 링크 (link-local scope) [https]RFC4291[], [https]RFC7346[]
3 예약 (reserved) / 로컬 범위의 서브넷 (subnet) / Realm-Local scope [https]RFC4291[], [https]RFC7346[]
4 로컬 범위의 관리 (admin-local scope) [https]RFC4291[], [https]RFC7346[]
5 로컬 범위의 사이트 (site-local scope) [https]RFC4291[], [https]RFC7346[]
6 할당되지 않음 (unassigned)
7 할당되지 않음 (unassigned)
8 로컬 범위의 조직 (organization-local scope) [https]RFC4291[], [https]RFC7346[]
9 할당되지 않음 (unassigned)
10 할당되지 않음 (unassigned)
11 할당되지 않음 (unassigned)
12 할당되지 않음 (unassigned)
13 할당되지 않음 (unassigned)
14 글로벌 범위 (global scope) [https]RFC4291[], [https]RFC7346[]
15 예약 (reserved) [https]RFC4291[], [https]RFC7346[]

프로그래밍 관점에서의 포팅

IPv6로의 포팅에서의 주요 핵심 키워드 AF_INET, AF_INET6, PF_INET, PF_INET6, IPPROTO_IP, IPPROTO_IPV6, IP_ADD_MEMBERSHIP, IPV6_JOIN_GROUP, IP_TTL, IP_MULTICAST_TTL, IPV6_UNICAST_HOPS, IPV6_MULTICAST_HOPS, struct sockaddr_storage, struct sockaddr_in, struct sockaddr_in6, struct in_addr, struct in6_addr, getaddrinfo, struct addrinfo

  • getaddrinfo 사용예제
  • TCP 연결 흐름구조
    iec_sockaddr_t s_remote, s_bind;
    iec_socket_t s_socket;
    iec_const_string_t s_remote_address;
    iec_socket_t s_socket;
    
    s_remote_address = "http://test.minzkn.com:80/index.php";
    
    /* 먼저 remote 주소의 sockaddr을 설정한다. (node는 uri를 허용한다) */
    if(iec_xapi_resolve_sockaddr(AF_UNSPEC, (iec_sockaddr_t *)(&s_remote), s_remote_address, iec_xapi_int_const(80), def_iec_true) == def_iec_true) {
        /* 설정된 s_remote의 family를 bind address family 로 결정한다. (node는 uri를 허용하지 않는다) */
        if(iec_xapi_resolve_sockaddr(s_remote.ss.ss_family, (iec_sockaddr_t *)(&s_bind), def_iec_null_string, iec_xapi_int_const(0), def_iec_false) != def_iec_error) {    
            /* 결정된 bind address family에 맞는 소켓을 연다. */
            s_socket = iec_xapi_socket(s_bind.ss.ss_family, SOCK_STREAM, IPPROTO_TCP);
            if(s_socket != def_iec_invalid_socket) {
                if(iec_xapi_bind(s_socket, (iec_sockaddr_t *)(&s_bind)) == def_iec_true) {
                    if(iec_xapi_connect(s_socket, (iec_sockaddr_t *)(&s_remote), s_timeout) == def_iec_true) {
                        /* connected */
                    }
                }
    
                s_socket = iec_xapi_closesocket(s_socket);
            }
        }
    }
    
    


  • TCP listen과정
    iec_const_string_t s_bind_address;
    iec_int_t s_bind_port;
    iec_sockaddr_t s_sockaddr_bind, s_sockaddr_accept;
    iec_socket_t s_listen_socket, s_accept_socket;
    iec_socklen_t s_socklen;
    
    if(iec_xapi_resolve_sockaddr(AF_UNSPEC, (iec_sockaddr_t *)(&s_sockaddr_bind), s_bind_address, s_bind_port, def_iec_false) == def_iec_true) {
        s_listen_socket = iec_xapi_socket(s_sockaddr_bind.ss_family, SOCK_STREAM, IPPROTO_TCP);
        if(s_listen_socket != def_iec_invalid_socket) {
            if(iec_xapi_bind(s_listen_socket, (iec_sockaddr_t *)(&s_sockaddr_bind), (iec_socklen_t)sizeof(s_sockaddr_bind)) == def_iec_true) {
                if(iec_xapi_listen(s_listen_socket, def_iec_max_backlog) == def_iec_true) {
                    for(; /* exit condition */ ;) {
                        s_socklen = (iec_socklen_t)sizeof(s_sockaddr_accept);
                        s_accept_socket = iec_xapi_accept(s_listen_socket, (iec_sockaddr_t *)(&s_sockaddr_accept), (iec_socklen_t *)(&s_socklen), 0/* timeout */);
                        if(s_accept_socket != def_iec_invalid_socket) {
                            if(s_sockaddr_accept.ss_family == AF_INET) {
                                /* IPv4 accept process */
                            }
                            else if(s_sockaddr_accept.ss_family == AF_INET6) {
                                /* IPv6 accept process */
                            }
                            else {
                                /* Unspec accept process */
                            }
                            iec_xapi_closesocket(s_accept_socket);
                        }
                        else {
                            iec_xapi_load_balance();
                        }
                    }
                }
            }
            iec_xapi_closesocket(s_listen_socket);
        }
    }
    


  • Multicast join
    • IPv4 multicast join : IPv4에서는 interface의 IP를 이용하여 join한다.
      struct ip_mreq
      {
              struct in_addr imr_multiaddr;   /* IP multicast address of group */
              struct in_addr imr_interface;   /* local IP address of interface */
      };
      
      setsockopt(socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (struct ip_mreq *)(&r), sizeof(struct ip_mreq));
      


    • IPv6 multicast join : ifindex 로 interface를 join한다는 점이 IPv4와 다름.
      struct ipv6_mreq {
              /* IPv6 multicast address of group */
              struct in6_addr ipv6mr_multiaddr;
      
              /* local IPv6 address of interface */
              int             ipv6mr_ifindex;
      };
      
      setsockopt(socket, IPPROTO_IPV6, IPV6_JOIN_GROUP, (struct ipv6_mreq *)(&r), sizeof(struct ipv6_mreq));
      


  • UDP TTL(HOPS)
    • IPv4에서의 TTL
      int s_ttl;
      
      /* unicast ttl */
      setsockopt(socket, IPPROTO_IP, IP_TTL, &s_ttl, sizeof(s_ttl));
      
      /* multicast ttl */
      setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, &s_ttl, sizeof(s_ttl));
      
    • IPv6에서의 HOPS
      int s_hops;
      
      /* unicast ttl */
      setsockopt(socket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &s_hops, sizeof(s_hops));
      
      /* multicast ttl */
      setsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &s_hops, sizeof(s_hops));
      


  • FTP protocol 의 확장
    기존의 PORT, PASV가 IPv4만을 위한 사양이기에 이에 대한 확장이 요구되었는데 새롭게 EPRT, EPSV로 정의된 명령이 추가되어 IPv6에서의 FTP data session을 명시할수 있게 되었습니다. FTP protocol의 IPv6에 관한 확장 프로토콜에 대한 표준은 rfc2428.txt 에서 명시하고 있습니다.
    EPRT<space><d><net-prt><d><net-addr><d><tcp-port><d>
    
    여기서 <d>는 구분자로써 '|'을 사용하는것으로 되어 있습니다.
    <net-prt>는 '1'은 IPv4를 의미하고 '2'는 IPv6를 의미합니다.
    
    EPSV<space><net-prt>
    229 Entering Extended Passive Mode (|||<tcp-port>|)
    
    EPSV에 대한 응답형식은 EPRT와 유사합니다. EPSV의 <net-prt>에는 '1', '2', 'ALL'이 사용될수 있다고 합니다. 하지만 실험결과 'ALL'은 제대로 동작하지 않는 경우가 많았습니다.
    


  • Linux Kernel 에서의 IPv6 extension header 접근에 대한 간략한 예시
    /*
    s_ipv6hdr : IPv6 Header에 대한 pointer 입력 
    s_nexthdr_ptr : nexthdr 가 명시된 부분에 대한 pointer 입력
    반환 : IPv6 extension header 의 크기 반환
    */
    size_t hwport_ipv6_extension_header_size(struct ipv6hdr *s_ipv6hdr, u8 *s_nexthdr_ptr)
    {
        size_t s_offset = (size_t)0; /* payload start offset == 40 */
        u8 *s_u8_ptr = (u8 *)(&s_ipv6hdr[1]); /* payload start entry */
        u8 s_nexthdr = s_ipv6hdr->nexthdr;
        struct ipv6_opt_hdr *s_exthdr;
        size_t s_exthdr_size;
    
        while(ipv6_ext_hdr(s_nexthdr)) {
            if(s_nexthdr == NEXTHDR_NONE) {
                break;
            }
    
            if(unlikely((s_offset + sizeof(struct ipv6_opt_hdr)) > ((size_t)s_ipv6hdr->payload_len))) {
                /* invalid extension header coding */
                s_offset = (size_t)s_ipv6hdr->payload_len;
                s_nexthdr = NEXTHDR_NONE;
                break;
            }
    
            s_exthdr = (struct ipv6_opt_hdr *)(&s_u8_ptr[s_offset]);
            if(s_nexthdr == NEXTHDR_FRAGMENT) {
                s_exthdr_size = (size_t)8u;
            }
            else if(s_nexthdr == NEXTHDR_AUTH) { /* AH header */
                s_exthdr_size = (s_exthdr->hdrlen + 2) << 2;
            }
            else {
                s_exthdr_size = ipv6_optlen(s_exthdr);
            }
    
            if(unlikely((s_offset + s_exthdr_size) > ((size_t)s_ipv6hdr->payload_len))) {
                /* invalid extension header size coding */
                s_offset = (size_t)s_ipv6hdr->payload_len;
                s_nexthdr = NEXTHDR_NONE;
                break;
            }
    
            s_nexthdr = s_exthdr->nexthdr;
            s_offset += s_exthdr_size;
        }
    
        if(s_nexthdr_ptr != ((u8 *)0)) {
            *s_nexthdr_ptr = s_nexthdr;
        }
    
        return(s_offset);
    }
    
    /*
    s_ipv6hdr : IPv6 Header에 대한 pointer 입력 
    s_transport_size_ptr : Transport layer 의 크기를 반환 (IPv6 Header 및 IPv6 extension header를 제외한 IPv6 payload 의 크기)
    s_nexthdr_ptr : nexthdr 가 명시된 부분에 대한 pointer 입력
    반환 : Transport layer에 대한 pointer 반환
    */
    void *hwport_ipv6_transport_entry(struct ipv6hdr *s_ipv6hdr, size_t *s_transport_size_ptr, u8 *s_nexthdr_ptr)
    {
        u8 *s_u8_ptr = (u8 *)s_ipv6hdr;
        size_t s_header_size = sizeof(struct ipv6hdr);
        size_t s_ext_header_size = hwport_ipv6_extension_header_size(s_ipv6hdr, s_nexthdr_ptr);
    
        if(s_transport_size_ptr != ((size_t *)0)) {
            *s_transport_size_ptr = ((size_t)s_ipv6hdr->payload_len) - s_ext_header_size;
        }
    
        return((void *)(&s_u8_ptr[s_header_size + s_ext_header_size]));
    }
    
    /* 사용예 */
    struct sk_buff *skb; /* 인입된 패킷을 담고 있는 skbuff pointer */
    size_t s_ipv6h_size = sizeof(struct ipv6hdr); /* IPv6 Header 크기 (IPv6 extension header는 제외) */
    struct ipv6hdr *s_ipv6hdr = ip_hdr(skb); /* skb로부터 IPv4/IPv6 Header 위치 pointer */
    
    u8 s_nexthdr; /* IPv6 externsion header 를 건너뛰고 마지막 nexthdr */
    size_t s_ipv6h_ext_size; /* IPv6 extension header 크기 */
    void *s_transport_entry; /* Transport layer entry pointer */
    size_t s_transport_size; /* Transport layer 크기 */
    
    ASSERT(s_ipv6hdr->version == 6);
    
    s_ipv6h_ext_size = hwport_ipv6_extension_header_size(s_ipv6hdr, (u8 *)(&s_nexthdr));
    s_transport_entry = hwport_ipv6_transport_entry(s_ipv6hdr, (size_t *)(&s_transport_size), (u8 *)(&s_nexthdr));
    if(s_nexthdr == IPPROTO_TCP) {
        struct tcphdr *s_tcphdr = (struct tcphdr *)s_transport_entry;
    }
    else if(s_nexthdr == IPPROTO_UDP) {
    }
    else {
        ...
    }
    


참고자료




/*
[ FrontPage | PrintView | RawView | RSS ]

Copyright ⓒ MINZKN.COM
All Rights Reserved.

MINZKN
*/