티스토리 뷰


어찌보면 좀 쌩뚱 맞을 수도 있는 64bit로 compile된 kernel과 32bit로 compile된 iptables 조합입니다. 일단 iptables쪽에는 기본적인 명령이나 동작에는 지장이 없는데, 문제는 바로 ip6tables 요놈입니다. 문제는 ip6tables --list 명령에서부터 시작되었습니다.

$ ip6tables -nL
ip6tc_init: valiget_entries: 1980 != 1984
d_hooks=0x0000000e, num_entries=4, size=1944
modprobe: cannot parse modules.dep
ip6tables v1.3.7: can't initialize ip6tables table `filter': Invalid argument
Perhaps ip6tables or your kernel needs to be upgraded.


위 출력은 기본적으로 kernel 에서 debuging을 위해 duprintf를 활성화 시켜주었고, 이로 인해 찍힌 문구를 보면 아래와 같습니다.

get_entries: 1980 != 1984

척봐도 뭔가 size가 맞지 않는것입니다. 그 size라고 하면 kernel과 iptables가 함께 사용하는 구조체에서 비롯된 것일텐데, 어떤 구조체에서 문제가 되었을까요? 첫 번째로 드는 생각은 위의 messeage가 출력된 곳에서 살펴보면 간단히 문제가 해결 될거라 여겼습니다. 32bit와 64bit의 호환이 정상적으로 되지 않은것일테니... 문제가 되는 구조체의 요소에 분명 long이나, pointer가 있을법 했습니다. (LP64처럼 Long type과 Pointer가 32bit에서는 4byte, 64bit에서는 8byte로 동작)

지나가는 얘기로 실제 공용(kernel과 iptables)으로 사용되는 구조체중 ipt_entry 구조체 내에 xt_counters는 unsigned long type을 요소로 갖는데요, 충분히 문제가 될 만한 부분입니다.

struct xt_counters {
     __u64 pcnt, bcnt;                       /* Packet and byte counters */
};

다시 본론으로 돌아와서 문제가 되는 구조체를 살펴보았지만 특별한 문제점은 보이지 않았습니다. 해당 구조체는 아래와 같습니다.

/* The argument to IP6T_SO_GET_ENTRIES. */
struct ip6t_get_entries {
    /* Which table: user fills this in. */
    char name[XT_TABLE_MAXNAMELEN];
   
    /* User fills this in: total entry size. */
    unsigned int size;
   
    /* The entries. */
    struct ip6t_entry entrytable[0];
};

XT_TABLE_MAXNAMELEN은 32로 define되어 있으며 얼핏 보아도 이 구조체의 size는 char배열 32byte + int 4byte로 36byte로 보입니다. 문제는!! 64bit로 compile된 kernel을 우습게 보았다는것. 분명 iptables가 참조하는 kerenl내의 ip6t_get_entries 구조체는 36 byte의 size를 갖습니다. 다만 64bit kernel은 구조체 size를 40byte로 인식합니다.

서론을 좀 거창하게 시작했는데 이유는 간단합니다. 32bit kernel의 경우 struct의 align을 4byte로 갖고, 64bit는 8byte로 갖기 때문입니다. 좀 더 쉽게 말하자면 32bit kernel에서 ip6t_get_entries 구조체는 아래와 같은 메모리 구조를 갖습니다.



align 되기를 4byte 기준이기 때문에 메모리에 낭비가 없는 구조입니다. 반면 64bit는 위의 언급처럼 8byte 기준으로 align되어 unsigned int로 선언된 size가 8byte 공간을 독차지합니다. 고로 결국 전체적인 그림을 보면 아래와 같이 구조체는 40byte를 차지해버립니다. (실제 size변수는 4byte지만)



이처럼 구조체를 다룰때는 메모리적인 문제가 발생되지 않도록 pack하는것이 중요한데, 구조체에 __attribute__ ((packed)) 과 같은 문장으로 중괄호를 닫음으로써 pack할 수 있습니다. 아래의 예제처럼 사용하도록 합시다,

struct RR_RR_s {
    char flags[1];
} __attribute__ ((packed));


pack의 효과는 32bit kernel 처럼 4byte 기준으로 구조체를 align함으로써 iptables에서 참조할때 문제가 되었던 get_entries: 1980 != 1984 부분을 해결 할 수 있습니다. 하지만 pack을 사용, 구조체의 size 문제를 피해 갔다고 해서 32bit ip6tables를 64bit kernel에 올리는 것의 모든 문제가 해결되는 것은 아닙니다.

이어서 발생할 수 있는 문제는 iptables source 곳곳에 존재하는 ALIGN 입니다, 기본적으로 4byte로 align을 제공하는 이 define function은 Makefile을 조작함으로써 피해갈 수 있습니다. IP6T_MIN_ALIGN=8 과 같이 적절한 내용의 정의를 추가하도록 합시다. 언급한 pack과 iptables Makefile의 조작으로 모든 문제가 해결되는 것은 아니지만.. 오늘의 포스팅은 여기까지만.... (이정도만으로도 충분한 진도를 나갔고 향후 발생되는 error는 어렵지 않게 잡을 수 있습니다.)

모든 개발자가 64bit 환경으로의 자유로운 변화를 겪는 그날이 오겠지요...

'개발 > Linux' 카테고리의 다른 글

__alignof__, aligned 매크로  (0) 2011.12.02
Kernel Netfilter socket option (getsockopt, setsockopt)  (2) 2011.12.02
find command  (0) 2011.11.29
비용이 낮은 연산자 선택 [%->&, / -> >>]  (0) 2011.11.20
pages_to_mb  (0) 2011.11.02
댓글
댓글쓰기 폼