이 문제는 어쩌다가 일어난다. 혹시 이런 문제에 고생하는 분에게 힌트가 되었으면 한다.

 

문제

아파치의 RewriteRule을 이용하여 특정서버로 redirect를 하는데, 한쪽 서버에만 접근하는 문제가 발생했다.

A클래스 타입의 ip만 접근하고, B클래스 타입의 ip는 접근하지 않는다.

 

해결

아파치 내부에서 gethostbyname함수를 사용하도록 한다.

 

자세한 내용.
ipv6 의 스펙 문서 "Default Address Selection for IPV6"를 참조한다. ftp://ftp.rfc-editor.org/in-notes/rfc3484.txt
RFC3484 를 의거하면, DNS 에서 돌려주는(round robin) IP 가 어떤 variation 을 갖느냐에 따라서 한쪽으로 몰릴 수 있다.
다음은 RFC3484의 Rule 9이다.

 

Rule 9:  Use longest matching prefix.
   When DA and DB belong to the same address family (both are IPv6 or both are IPv4): If CommonPrefixLen(DA, Source(DA)) > CommonPrefixLen(DB, Source(DB)), then prefer DA.  Similarly, if CommonPrefixLen(DA, Source(DA)) < CommonPrefixLen(DB, Source(DB)), then prefer DB.

 

즉, 주어진 아이피에 따라  getaddrinfo메소드는  특정 ip대역만을 얻어오게 한다. 따라서 캐쉬 서버 한 대만 줄기차게 요청하는 문제가 생겼다.
(A클래스, B클래스의 ip가 존재하면, A클래스 ip만 준다.)

 

사실 사람들이 이미 고민을 하고 있다.
http://people.redhat.com/drepper/linux-rfc3484.html

내부 소스를 까보면, xor를 하고 있는 구조로 되어 있다.


glibc 의 rfc3484_sort 함수를 보면 다음과 같다.

static int
rfc3484_sort (const void *p1, const void *p2)
{
  const struct sort_result *a1 = (const struct sort_result *) p1;
  const struct sort_result *a2 = (const struct sort_result *) p2;

  ...

  /* Rule 9: Use longest matching prefix.  */
  if (a1->got_source_addr
      && a1->dest_addr->ai_family == a2->dest_addr->ai_family)
    {
      int bit1 = 0;
      int bit2 = 0;

      if (a1->dest_addr->ai_family == PF_INET)
        {
          assert (a1->source_addr.ss_family == PF_INET);
          assert (a2->source_addr.ss_family == PF_INET);

          struct sockaddr_in *in1_dst;
          struct sockaddr_in *in1_src;
          struct sockaddr_in *in2_dst;
          struct sockaddr_in *in2_src;

          in1_dst = (struct sockaddr_in *) a1->dest_addr->ai_addr;
          in1_src = (struct sockaddr_in *) &a1->source_addr;
          in2_dst = (struct sockaddr_in *) a2->dest_addr->ai_addr;
          in2_src = (struct sockaddr_in *) &a2->source_addr;

          bit1 = fls (ntohl (in1_dst->sin_addr.s_addr
                             ^ in1_src->sin_addr.s_addr));
          bit2 = fls (ntohl (in2_dst->sin_addr.s_addr
                             ^ in2_src->sin_addr.s_addr));
        }
      else if (a1->dest_addr->ai_family == PF_INET6)
         {

         ...

         }

      if (bit1 > bit2)
        return -1;
      if (bit1 < bit2)
        return 1;
    }

/* Rule 10: Otherwise, leave the order unchanged.  */
  return 0;
}

 

 

          bit1 = fls (ntohl (in1_dst->sin_addr.s_addr
                             ^ in1_src->sin_addr.s_addr));
          bit2 = fls (ntohl (in2_dst->sin_addr.s_addr
                             ^ in2_src->sin_addr.s_addr));

 

A클래스의 ip와 B클래스의 ip 소팅시, 항상 A클래스만 줄 수 있는 구조이다.

 

따라서 gethostbyname을 쓰도록 해서, 사용해야 한다. 아파치 설치시
/home/www/src/httpd-2.0.52/srclib/apr/include/arch/unix/apr_private.h 파일에서 해야할 일이 있다.
HAVE_GETNAMEINFO, HAVE_GETADDRINFO이 선언된 부분을 삭제한다.
sed  -i -e '/HAVE_GETADDRINFO/d' -e '/HAVE_GETNAMEADDR/d' srclib/apr/include/arch/unix/apr_private.h

이렇게 되면, 캐쉬서버를 여러 개를 볼 수 있어서 캐쉬서버가 문제가 없도록 할 수 있다.

 

이렇게 설치하고 나서 netstat를 하게 되면,

Posted by 김용환 '김용환'

댓글을 달아 주세요