레이블이 Network인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Network인 게시물을 표시합니다. 모든 게시물 표시

iptables 와 netfilter 이해


!!! Caution 잘못된 번역이나 이해로 틀릴 수 있음!!! ^^;;

[iptables, netfilter 은 뭘까?]

netfilter 는 네트워킹의 모든 패킷들에 의해 트리거되는 패킷 필터링 hook 이다.
iptables 는 커널의 netfilter(패킷 필터 프레임워크) 인터페이스를 사용해 방화벽 규칙을 구성한다.


[netfilter Hooks]

프로그램에 등록할 수 있는 netfilter hook 에는 아래 5개가 있다.
네트워크 스택을 통과할때 아래 5개의 hook 들을 등록한 커널 모듈에 의해서 트리거 된다.
incoming/outgoing 패킷에 따라 트리거 되거나 되지 않는다.
그리고 이전 지점에선 dropped 되거나 rejected 된다.

NF_IP_PRE_ROUTING: 이 hook 은 네트워크 스택에서 들어오고 나서 바로 incoming 트래픽에 의해 트리거된다. 라우팅 결정되기전에 처리된다.
NF_IP_LOCAL_IN: 이 hook 은 incoming 패킷이 라우팅된 후, 라우팅 목적지가 로컬 시스템인 경우 트리거 된다.
NF_IP_FORWARD: 이 hook 은 incomming 패킷이 라우팅된 후, 라우팅 목적지가 로컬이 아닌 다른 호스트인 경우 트리거 된다.
NF_IP_LOCAL_OUT: 이 hook 은 로컬에서 생성된 outbound(외부로 나가는) 트래픽이 네트워크 스택을 만나면 트리거 된다.
NF_IP_POST_ROUTING: 이 hook 은 outgoing,fowrard 트래픽이 네티워트에 위치하기 직전에 트리거 된다.

커널 모듈은 이 hook 들이 트리거 됐을때 우선순위를 결정하기 위해 priority number 를 제공해야 한다.
이는 멀티 모듈(또는 모듈 하나의 멀티 인스턴스)에서도 각각의 hook 의 우선순위 결정을 의미한다.
각 모듈은 순서대로 호출되고 패킷 어떻게 처리되어야 하는지 나타낸 결정이 netfilter 에 리턴된다.


netfilter hook 그림을 보는게 이해하기에 좋다.



[iptables 테이블과 체인]
iptables 방화벽은 rule(규칙)을 구성하기 위해 테이블을 사용한다.
이 테이블은 만들고자 하는 형태에 따라 구분된다.
예를 들어 network address translation 관련한것이라면, nat 테이블에 놓여진다.
만약 패킷이 목적지까지 허용할지 말지를 결정하기 위해서는 filter 테이블에도 추가된다.

iptables 테이블 내에서 규칙은 chain(체인) 으로 나뉘어져 조직화된다.
테이블들이 일반적인 규칙의 목적에 따라 정의 되는 반면, built-in(빌트인) 체인은 규칙들을 트리거하는 netfilter hook 을 나타낸다.
기본적으로 체인은 언제 evalute(평가)될지 결정한다.

아래는 빌트인 체인 이름으로 netfilter hook 과 연관된 이름을 반영한다.

PREROUTING : NF_IP_PRE_ROUTING hook 에 의해 트리거
INPUT : NF_IP_LOCAL_IN hook 에 의해 트리거
FORWARD : NF_IP_FORWARD hook 에 의해 트리거
OUTPUT : NF_IP_LOCAL_OUT hook 에 의해 트리거
POSTROUTING : NF_IP_POST_ROUTING hook 에 의해 트리거

체인들은 관리자가 패킷의 delivery path 에 규칙이 어디로 평가되어질지를 컨트롤할 수 있도록 허용한다.
때문에 각 멀티 체인을 갖는 테이블은 처리중 여러 지점에서 영향을 받을 수 있다.
왜냐하면 결정의 특정 타입은 오직 네티워크 스택의 특정 지점에서 의미가 있기 때문에 모든 테이블은 각 커널 hook 에 등록된 체인을 갖지 못 할 수 있다.

5개의 netfilter kernel hook 있고 멀티 테이블을 가진 체인들은 각각 hook 에 등록된다.
예를 들어 3개의 테이블이 PREROUTING 을 가지고 있고, 이 체인들이 NF_IP_PRE_ROUTING hook 으로 등록되어 있으면� 이 체인들은 각 테이블의 PREROUTING 체인이 호출될 때의 우선순위를 명시한다.
우선 순위가 가장 높은 PREROUTING 체인내의 각 규칙 들은 다음 PREROUTING 체인으로 이동하기 전에 순차적으로 평가된다.
잠시 각 체인들의 구체적인 순서를 살펴보자.


[사용 가능한 테이블과 각 테이블에서 구현된 체인]

각 chain 에 맞는 다음과 같은 성격의 규칙 테이블이 있다.
THE FILTER TABLE: iptables 에서 가장 많이 사용되는 테이블로, 패킷을 목적지로 보내는것을 허용할지 여부를 판단한다.
방화벽 용어로 'filtering' 패킷 으로 알려져 있다.패킷 필터링에 사용하는 테이블, INTPUT,FORWARD,OUTPUT chain 에서 사용
THE NAT TABLE: 출발지, 목적지 변경시 사용하는 테이블, PREROUTING,POSTROUTING chain 에서 사용
THE MANGLE TABLE: 패킷 변조시 사용하는 테이블�, 5개 chain 모두에서 사용
THE RAW TABLE: 주로 netfilter 에서 추적하지 않도록 NOTRACK 설정하는데 사용하는 테이블, PREROUTING,OUTPUT chain 에서 사용
THE SECURITY TABLE: SELinux 보안 사용시 사용하는 테이블, INPUT,FORWARD,OUTPUT chain 에서 사용


[체인 순회 순서]

방화벽이 전송을 허용한다면 다음 흐름은 각각에 상황에서 traverse(순회�) path 를 나타낸다.

incoming 패킷이 목적기자 로컬인 경우: PREROUTING -> INPUT
incoming 패킷이 목적지가 다른 호스트인 경우: PREROUTING -> FORWARD -> POSTROUTING
로컬에서 생성된 패킷: OUTPUT -> POSTROUTING


[iptables 규칙]

규칙은 특정 테이블안애 특정 체인에 놓인다.
각 체인은 호출될때 패킷은 체인 순서대로 각 규칙에 대해 체크한다.
각 규칙은 matching 와 action 구성 요소를 가진다.

matching(매칭)
매칭은 패킷이 액션(또는 target)과 순서대로 만나야 하는 기준을 명시한다.
매칭 시스템은 매우 유연하고 시스템의 사용가능한 iptables 로 확장될 수 있다.
규칙들은 프로토콜타입,출발/목적지 주소/포트,input/output 인터페이스, 헤더, 연결 상태등의 기준으로 구성된다.
다른 트래픽들과 구분하기 위해 규칙 집합(rule sets)으로 결합될 수 있도 있다

targets
타겟은 패킷인 규칙의 매칭을 만났을때 트리거 되는 액션이다.
타겟은 다음 2개의 카테고리로 나뉜다.
- terminating targets: 체인내 평가를 끝내고 netfilter 제어권을 리턴한다. 리턴된 값에 따라 drop 또는 allow 후 다튼 단계로 처리를 이어간다.
- non-terminating targets: 체이내 평가가를 계속 수행한다.

[사용자 정의 체인으로 점프]

non-terminating target 중 jmp target은 추가 처리를 위해 다른 체인으로 이동하는 액션이다.
규칙들은 빌트인 체인이 놓여지는 방식 처럼 사용자 정의 체인에 놓여질 수 있다.
사용자 정의 체인은 빌트인 체인과는 다르게 규칙으로 부터 jumping 으로만 접근할 수 있다.
(사용자 정의 체인은 netfilter hook 에 등록되어 있지 않다.)

사용자 정의 체인들은 이를 호출하는 체인의 단순한 확장 방식으로 동작한다.
예를 들어 사용자 정의 체인에서 규칙 리스트의 끝에 도달하거나 리턴 대상이 매칭 규칙에 의해 활성화되면 평가는 체인을 호출해 통과한다.
평가는 추가적인 사용자 정의 체인들로 점프할 할 수 있다.


[iptables 과 connection tracking(추적)]

connection tracking 은 iptables 이 ongoing connection 의 context 에 보여진는 패킷들 대해서 결정하도록 허용한다.
connection tracking system 은 stateful 한 오퍼레이션을 수행하는데 필요한 기능을 제공한다.

connection tracking 은 패킷이 네트워크 스택에 들어온 후 바로 적용된다.
raw table 체인과 기본적인 체크들은 패킷과 하나의 connection 을 연결하기 전에 패킷에 수행되는 유일한 로직이다.

시스템은 각 패킷들을 존재하는 connection 들 집합과 체크한다.
만약 새로운 connections 이 필요하다면 connection의 상태를 업데이트한다.
raw 체인의 NOTRACK 으로 마크된 패킷들은 connection tracking 라우팅으로 바이패스된다.

시스템은 각각의 패킷들을 현재 존재하는 connection들의 집합과 체크하고, 그것은 connection의 state를 업데이트 한다. 필요할 때 시스템에 새로운 connection을 추가한다. 패킷들은 하나의 raw 체인에서  NOTRACK tarket으로 표시된다. 그리고 connection tracking 경로를 bypass 한다.

Available States
connections tracking 시스템에 의해 connection tracked 는 다음 상태중 하나가 된다.

NEW: 존재하는 connection 연결되지 않는 패킷이 도착할때, 유효하지 않는 첫번째 패킷일때, 새로운 connection 은 NEW label이 추가된다. TCP(커넥션인지),UDP(비연결적인) 프로토콜 모두에서 발생한다.

ESTABLISHED: 유효한 응답을 받았을때 NET->ESTABLISHED 로 변한다. TCP 에서는 SYN/ACK, UDP와ICMP 에서는 트랙픽으로 원래 패킷의 출발지와 목적지가 바뀌는 응답을 의미한다.

RELATED: 패킷이 존재하는 connection 의 일부가 아니지만 시스팀에서 이미 connection 에 연결되어 있따면 RELATED label 이 된다. 이 상태는 helper connection 으로 FTP 데이터 전송 connection 이나 ICMP 응답 connections 시도하는 케이스 이다.

INVALID: 존재하는 connection 과 연결되지 않고 새 connection 을 open 하기에도 적절하지 않은 경우, indentified 될 수도 없고 다른 이유로 라우팅 되지 않는 경우, INVALID 로 마크된다.

UNTRACKED: raw table chain 에 트래킹(추적)을 바이패스하는 타겟으로 된경우 UNTRACKED로 마크된다.

SNAT: 출발 주소가 NAT 연산에 의해서 변경되면 가상 상태가 설정된다. connection tracking system 에 의해 사용되어지며, 출발지 주소가 응답 패킷에서 변경되는지를 알 수 있다.

DNAT: 목적지 주소가 NAT 연산에 의해 변경되면 가상 상태가 설정된다.  tracking system 에 의해 사용되어지며, 목적지 주소가 응답 패킷에서 변경되는지를 알 수 있다.

connection tracking system 은 관리자가 connection의 lifetime 에 있어 특정 시점을 타겟으로 하는 규칙을 만들 수 있도록 허용하고 state 들을 추적한다.


[결론]
netfilter 패킷 필터링 프레임워크가 와 iptables 방화벽은 리눅스 서버에서 대부분의 방화벽 솔루션의 기본이다.
netfilter kernel hook 은 시스템에서 처리되는 패킷들의 강력한 제어를 제공하는 네트워킹 스택에 가깝다.
iptables 방화벽은 유연하고 확장가능한 통신 정책 요구사항 방법을 커널에 제공하는 netfilter 기능을 기반으로 한다.
이 글을 같이 보면서 여러분은 netfilter와 iptables을 보다 잘 다룰 수 있어 여러분의 시스템을 보다 안전하게 할 수 있을 겁니다.

container 에 없는 iptables 커맨드 실행하기

# container 에 없는 iptables 커맨드 실행하기

# 방법1
# container 시작시 호스트 볼륨 연결하기
# 이미 컨테이너가 존재하면 삭제(--rm)
# 호스트의 root path 를 컨테이너의 /host 로 연결하고, (--volume /:/new_root)
# 컨테이너에게 확장권한을 부여한다.(--privileged)
# 컨테이너 실행시 /new_root 로 root 를 변경한다.
docker run -it \
--name=ysoftman
--hostname=ysoftman \
--rm \
--volume /:/new_root \
--privileged  \
busybox \
chroot /new_root

# 이제 컨테이너 내에서 호스트의 커맨드가 실행된다.
iptables -t filter -S


# 방법2
# 컨테이너 내에서 --pid=host 로 호스트 PID namespace 를 사용한다.
# 호스트의 pid 1(system) 대상으로(-t) -m(--mount[=file], 파일을 명시하지 않으면 target process 의 mount namespace 조인)
docker run -it \
--name=ysoftman \
--hostname=ysoftman \
--rm \
--privileged \
--pid=host \
busybox \
nsenter -t 1 -m sh

# 이제 컨테이너 내에서 호스트의 mount 정보로 호스트의 커맨드가 실행된다.
iptables -t filter -S


# 방법3
# 그냥 컨테이너 실행
docker run -it --name=ysoftman --hostname=ysoftman busybox

# 터미널을 하나 더 열고 호스트에서
# 위 컨테이너의 pid 의 network namespace 조인해 iptables 명령 실행한다.
# -t(--target) pid -n(--net[=file], 파일을 명시하지 않으면 target process의 네트워크 네임스페이스로 조인)
nsenter -t $(docker inspect --format {{.State.Pid}} ysoftman) -n iptables -t filter -S

linux namespace

# 2002년 리눅스 커널에 포함된 namespace(mnt, pid, net, ipc, cgroup...타입) 로 isolation 구현
# 모든 프로세스들은 특정 네임스페이스에 속하고 unshare 로 새로운 네임스페이스에서 프로세스를 실행할 수 있다. 네임스페이스는 exit 로 빠져나올 수 있다.
# unshare 시 프로세스를 명시하지 않으면 ${SHELL}값(/bin/sh 또는 /bin/bash등)이 디폴트로 실행된다.

#####

# mnt namespace
# 부모(호스트)로 부터 격리된 mount타입(-m, --mount)의 네임스페이스로 /bin/bash 프로그램 실행
unshare --mount /bin/sh

# mount_ns 에 마운트하면 현재 네임스페이스에서만 마운트된것을 알 수 있다.
# tmpfs : 가상 메모리 파일시스템
# ushare 된 마운트 스페이스(격리됨)에서 생성한 마운트는 호스트에서 보이지 않는다.
mkdir /tmp/mount_ns
mount -n -t tmpfs tmpfs /tmp/mount_ns
mount | grep mount_ns

# 터미널을 하나 더 띄워 호스트 네임스페이스에서 보면 값이 다른것을 알 수 있다.
readlink /proc/$$/ns/mnt

# 호스트에서 tmpfs 마운트 정보가 안보이는것을 확인
mount | grep mount_ns

# 테스트 결과

#####

# pid namespace
# 새로운 pid(-p, --pid), -f(--fork) 네임스페이스으로 실행
# --mount-proc Just before running the program, mount the proc filesystem at mountpoint (default is /proc)
# --mount-prod 옵션을 사용하면 pid 네임스페이스가 /proc/{pid} 로 마운트되어 쉽게 pid 네임스페이스 접근할 수 있도록 해준다.(mount namspace 도 생긴다.)
unshare -fp --mount-proc /bin/sh

# 호스트 네임스페이스와 현재 pid 네임스페이스에 다른 pid 로 보인다.
# 현재 pid 네임스페이스에서 프로세스를 확인해보면 1번이다.
echo $$
ps -ef

# pid 네임스페이스에서의 pid namespace 정보를 보면
# 아래 호스트의 unshare mount namespace 와 다른것을 알 수 있다.
# unshare(mt ns id:4026532202) ---> pid-namespace /bin/sh (pid ns id:4026532203)
lsns -t pid -p 1

# 호스트에서 unshare 프로세스 pid 찾기
ps -ef | grep unshare

# ushare 프로세스의 namespace 정보
# ls -ahl /proc/{pid}/ns 로도 알 수 있다.
lsns -p {pid}

# mnt 네임스페이스ID 로 확인
lsns {namespace id}

# 테스트 결과 화면

#####

# cgroup namespace
# cgroup 는 controlgroup으로 프로세스의 리소스 제어하는 파일시스템이다.(sys/fs/cgroup)
# sleep 백그라운드 프로세스를 freezer(cgroup 작업 중지,재개를 위한 subsystem)의 sub2로 등록
mkdir -p /sys/fs/cgroup/freezer/sub2
sleep 100000 &
[1] 2932

# 테스트를 위해 sleep pid 환경변수로 설정 
export sleep_pid=2932

# sleep pid 를 sub2 cgroup 으로 등록
echo ${sleep_pid} > /sys/fs/cgroup/freezer/sub2/cgroup.procs

# 현재 프로세스도 freezer sub1 로 등록하자.
mkdir -p /sys/fs/cgroup/freezer/sub1

# 현재프로세스 확인, 테스트를 위해 현재 pid 환경변수로 설정 
echo $$
2178
export parent_pid=$(echo $$)

# 현재 프로세스 pid 를 sub1 cgroup 으로 등록
echo ${parent_pid} > /sys/fs/cgroup/freezer/sub1/cgroup.procs

# freezer (init process, PID 1), parent, sleep pid 확인
cat /proc/1/cgroup | grep freezer
cat /proc/${parent_pid}/cgroup | grep freezer
cat /proc/${sleep_pid}/cgroup | grep freezer

# freezer 파일 확인
tree -L 1 /sys/fs/cgroup/freezer/
/sys/fs/cgroup/freezer/
├── cgroup.clone_children
├── cgroup.procs
├── cgroup.sane_behavior
├── docker
├── notify_on_release
├── release_agent
├── sub1
├── sub2
└── tasks

# 새로운 cgroup(-C, --cgroup) 네임스페이스로 실행
unshare -Cm /bin/sh

# freezer (init process, PID 1), parent, sleep pid 확인해보면
# sub1 --> / 가 루트가 되고
# sub2 --> ../sub2 로 변경되었지만 호스트 cgroup 은 그대로 보인다.
cat /proc/1/cgroup | grep freezer
cat /proc/2178/cgroup | grep freezer
cat /proc/2932/cgroup | grep freezer

# 테스트 결과

#####

# stress 툴로 시스템 부하(cpu)를 테스트해보자.
# new_root 에 bash 준비하기
mkdir -p new_root/{bin,usr,lib,lib64} new_root/lib/x86_64-linux-gnu/ new_root/usr/lib/x86_64-linux-gnu
cp /bin/bash new_root/bin
cp /bin/mount new_root/bin
cp /lib/x86_64-linux-gnu/{libtinfo.so.5,libdl.so.2,libc.so.6} new_root/lib
cp /lib64/ld-linux-x86-64.so.2 new_root/lib64

# mount 명령등 준비하기
cp -rv /bin/{ls,cat,mount,umount,mkdir,rmdir,rm,cp,grep,ps} new_root/bin
cp -rv /usr/lib/x86_64-linux-gnu/* new_root/usr/lib/x86_64-linux-gnu
cp -rv /lib/x86_64-linux-gnu/* new_root/lib/x86_64-linux-gnu
cp -rv /lib64/* new_root/lib64

# stress, cgroup 툴 설치
apt-get install -y stress cgroup-tools

# chroot 후 필요한 파일들 복사
mkdir -p new_root/usr/bin/bin
cp /usr/bin/{top,stress,cgcreate,cgset,cgexec} new_root/usr/bin/

# top 실행시 필요한 파일들 복사
mkdir -p new_root/usr/share/terminfo
cp -r /usr/share/terminfo/ new_root/usr/share/terminfo

# cgcreate 시 root uid 파악에 필요
mkdir -p new_root/etc
cp /etc/group new_root/etc/group
cp /etc/passwd new_root/etc/passwd

# cgroup 관련 마운트 확인
mount | grep -E "cgroup*.cpu"

# chroot 후 mount 하기
chroot new_root
mkdir -p /sys/fs/cgroup
mkdir -p /sys/fs/cgroup/{cpuset,"cpu,cpuacct"}
mount -t cgroup -o cpuset cgroup /sys/fs/cgroup/cpuset
mount -t cgroup -o cpu,cpuacct cgroup /sys/fs/cgroup/cpu,cpuacct

# /proc 마운트
mkdir -p /proc
mount -t proc proc /proc

# mycgroup 생성 및 확인
cgcreate -a root -g cpu:mycgroup
ls -al /sys/fs/cgroup/cpu,cpuacct/ | grep mycgroup

# CPU 사용률(%) = (cpu.cfs_quota_us / cpu.cfs_period_us) * 100
# cpu.cfs_period_us 를 확인해 보면 100000(us, 100ms) 이다.
# cat /sys/fs/cgroup/cpu,cpuacct/cpu.cfs_period_us
# 따라서 cpu.cfs_quota_us=30000 을 주면 30% cpu 사용률로 제한한다.
cgset -r cpu.cfs_quota_us=30000 mycgroup

# mycgroup 에 strecc -c(--cpu) 1로 부하 테스트 백그라운드로 실행
cgexec -g cpu:mycgroup stress -c 1 &

# top 으로 stress 프로세스 30% 사용 확인
export TERM="xterm-256color"
top
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 3129 root      20   0    8248     96      0 R  30.0  0.0   0:03.50 stress

# 테스트 결과

#####

# net namespace
# 새로운 network(-n, --net) 네임스페이스으로 실행
unshare --net /bin/sh

# 호스트 네임스페이스와 정보가 다른것을 알 수 있다.
ip a

# 테스트 결과

# ip 로 net namespace 생성해보기
ip netns add mynet

# net namespace 확인
ip netns list
# 또는
ls /var/run/netns

# nsenter 로 net네임스페이스(--net)에 들어가기
# 별도 프로그램 명시하지 않으면 디폴트로 $SHELL -> /bin/bash 를 실행한다.
nsenter --net=/var/run/netns/mynet
ip a

# net 네임스페이스에서 현재 프로세스의 pid,user,ipc,mnt등의 네임스페이스 정보 확인
lsns -p $$

# 호스트에서 net 네임스페이스 id 확인
# readlink /proc/$$/ns/net 대신 lsns 명령어로도 네임스페이스 정보를 확일 수 있다.
# lsns -t 타입 -p pid
lsns -t net -p $$

# 테스트 결과

#####

# user namespace 생성
# 새로운 user(-U, --user) 네임스페이스으로 실행
unshare --user /bin/sh

# 새로운 user 네임스페이스에서의 사용자 정보 확인
whoami
id

# 네임스페이스 확인해보기
ls -al /proc/$$/ns
lsns -p $$

# 테스트 결과 화면

# container 와 호스트 root 가 같은것인지 확인해보기
# docker 로 busybox 컨테이너 띄우기
docker container run -it --name busybox --rm busybox

# ps 로 보면 root 로 실행됐고, uid=0 으로 되어 있다
ps -ef
id

# 현재 프로세스의 user 네임스페이스 확인
readlink /proc/$$/ns/user

# 호스트에서도 확인해보면 root 이름이고 uid=0 이다.
ps -ef | grep root | head -3
id

# 호스트에서 현재 프로세스의 user 네임스페이스 확인해보면 위 container user namespace id 와 같은것을 알 수 있다.
# 결국 container 의 root 도 호스트 user 네임스페이스를 공유하기 때문에 호스트 root 와 같은것이다.
readlink /proc/$$/ns/user

# 테스트 결과 화면

# 참고로 docker v1.10 이상 부터는 별도의 user namespace 를 지원하지만
기본적으로 docker 로 컨테이너를 실행하면 root 로 프로세스를 실행한다.(user namespace 를 별도로 만들지 않고 호스트를 user namespace 를 사용한다.)
# 이 때문에 container root 계정이 악용될 수 있음을 인지해야 한다.
# docker remap 설정으로 uid 를 다르게할 수 있다. 아래 별도 포스트로 정리

#####

# uts(unix time sharing system) namespace
# 새로운 uts(-u, --uts) 네임스페이스으로 실행
unshare --uts /bin/sh

# 호스트명 변경하고 확인
hostname ysoftman
hostname

# 터미널을 하나 더 띄워 호스트를 확인해 보면 영향을 주지 않는것을 알 수 있다.
# 테스트 화면

#####

# ipc(interprocess communication) namespace
# shared memory, queue, semaphore 등의 리소스에서 사용되며 같은 ipc 네임스페이스안 프로세스들은 ipc 통신할 수 있다.
# ipc (shared memory, pipe) 상태 정보 보기
# -m  Write information about active shared memory segments.
ipcs -m

# ipc shared memeory 리소스 1000byte 생성
# -m Create a shared memory segment of size bytes.
ipcmk -M 1000

# 1000 byte shared memory segemnt 가 생성된것을 확인할 수 있다.
ipcs -m

# 새로운 ipc(-i, --ipc) 네임스페이스 생성
unshare --ipc /bin/sh

# 새로운 ipc 네임스페이스에서는 호스트에서 만든 1000bytes 리소스가 보이지 않는다.
ipcs -m

# 호스트로 나오기
exit

# ipc 리소스 삭제
# -M Remove the shared memory segment created with shmkey after the last detach is performed.
ipcrm -M {shared memory segment key}

# 삭제된 리소스 확인 
ipcs -m

# 위 과정을 테스트한 결과

firefox esni, trr 기능

# https 암호화 전 접속 호스트명 SNI(Server Name Indication)가
# 암호화 되지 않아 ISP 에서 차단하는 사이트가 많은데,
# ESNI(Encrypted SNI)와
# TRR(Trusted Recursive Resolver)를 사용할 수 있는
# firefox 를 사용하면 차단 없이 접속 가능하다.

# firefox about 프로토콜에서 다음과 같이 설정한다.
about:config -> network.security.esni.enabled -> true
about:config -> network.trr.bootstrapAddress -> 1.1.1.1
about:config -> network.trr.uri -> https://mozilla.cloudflare-dns.com/dns-query
about:config -> network.dns.echconfig.enabled -> true
about:config -> network.trr.mode -> 3

# trr.bootstrapAddress(1.1.1.1) 로 trr.uri 주소를 파악하게 된다.
# trr.mode
# 0: native resolver (default)
# 1: native resolver, trr 중 응답 빠른것을 사용
# 2: trr 시도 후 안돼면 native resolver 사용
# 3: trr 만 사용
# 4: reserved(shadow mode 로 추가될 예정)

# firefox about 프로토콜 설명
https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/The_about_protocol

# 85버전 부터는 ESNI 대신 보안이 더 강화된 ECH(Encrypted Client Hello)를 사용한다.
# ECH 모드를 활성화 하려면 다음 2개 설정을 활성화 해야 한다.
about:config -> network.dns.echconfig.enabled -> true
about:config -> network.dns.use_https_rr_as_altsvc -> true

# ESNI -> ECH 설명에서 설정 방법 참고
https://blog.mozilla.org/security/2021/01/07/encrypted-client-hello-the-future-of-esni-in-firefox/

envoy proxy 사용하기

# envoy 는 프로시 서버로 요청을 원하는 곳으로 라우팅(포워딩)해준다.
# https://github.com/envoyproxy/envoy


##########


# mac 에서 envoy build
# brew llvm 버전 https://formulae.brew.sh/formula/llvm
brew install coreutils wget cmake libtool go bazel automake ninja llvm@7 autoconf

# 업그레이드 에러가 나오는 패키지들에 대해선 업그레이드를 수행 
brew upgrade coreutils wget cmake go ninja

# evnoy 소스 클론
git clone https://github.com/envoyproxy/envoy

# bazel 로 빌드
# worksapce 는 현재 envoy 소스 루트 디렉토리이고
# workspace 에서 상대위치 //source/exe 에 BUILD 파일이 있고
# BUILD 파일내에서 envoy-static 타겟으로 빌드
bazel build //source/exe:envoy-static

# bazel 빌드중 libz.a 실패해서 포기ㅠ


##########


# docker envoy 사용하기
# envoy 도커 이미지 받기
docker pull envoyproxy/envoy

# envoy 컨테이너 실행
# 디폴트로 9901 은 admin 접속하고, 10000 로 리슨하고 있다.
docker run --name envoy -p 9901:9901 -p 10000:10000 -dit envoyproxy/envoy

# envoy 컨테이너 삭제
docker rm -f envoy

# envoy 컨테이너 접속
docker exec -it envoy /bin/bash

# 디폴트 설정
cat /etc/envoy/envoy.yaml

# 10000 으로 요청하면 envoy 가 구글로 라우팅(포워딩) 한다.
http://127.0.0.1:10000/

# 이제 envoy.yaml 설정을 수정해 보자
# 참고로 docker envoy 이미지는 Ubuntu 버전이다.
# cat /etc/issue
# Ubuntu 16.04.6 LTS \n \l
# 최소한의 용량으로 vim 과 같을 툴은 기본적으로 설치되어 있지 않다.
# vim 을 설치하기 위해선 패키지 관리자 apt-get 을 업데이트하고 설치해야 한다.
# apt-get update && apt-get install -y vim
# vim 없으 아래명령으로 파일을 덮어 쓰자.
# daum.net 으로 라우팅을 변경해보자.
# admin 도 0.0.0.0 으로 모든 IP 에서의 접근을 허용하자.
cat > /etc/envoy/envoy.yaml << zzz
admin:
  access_log_path: /tmp/admin_access.log
  address:
    socket_address:
      protocol: TCP
      address: 0.0.0.0
      port_value: 9901
static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address:
        protocol: TCP
        address: 0.0.0.0
        port_value: 10000
    filter_chains:
    - filters:
      - name: envoy.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_service
              domains: ["*"]
              routes:
              - match:
                  prefix: "/"
                route:
                  host_rewrite: www.daum.net
                  cluster: service_daum
          http_filters:
          - name: envoy.router
  clusters:
  - name: service_daum
    connect_timeout: 0.25s
    type: LOGICAL_DNS
    # Comment out the following line to test on v6 networks
    dns_lookup_family: V4_ONLY
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: service_daum
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: www.daum.net
                port_value: 443
    tls_context:
      sni: www.daum.net
zzz


# 설정 파일을 수정 후 envoy 재시작한다.
# https://www.envoyproxy.io/docs/envoy/latest/operations/hot_restarter
# --restart-epoch 로 재시작을 할 수 있지만 프로세스를 중지(ctrl+c)하면 컨테이너가 종료된다.
# /usr/local/bin/envoy -c /etc/envoy/envoy.yaml --restart-epoch 1 -l trace

# 그래서 변경된 설정파일 적용을 위해선 evnoy container 를 재시작한다.
docker stop envoy
docker start envoy

mac wifi 속도 느려짐

macOS Mojave 10.14.2  사용중인데, 이상하게 wifi 속도가 느려지는 이슈가 있다.
맥을 리부팅하면 200 Mbit/s 이상의 속도가 나오다 어느정도 시간이 지나면
100 MBit/s
10 MBits/s
5 MBit/s
로 계속 떨어지고 결국에는 인터넷 서핑을 하기 힘들정도가 된다.

airport(wi-fi) 상태 정보를 확인해보면

/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I

RSSI(Received Signal Strength Indicator) 는 수신강도로 음수로 표시되며 값이 높을수록(0에 가까울수록) 감도가 좋다. -80  보다 크면 양호

예) -60 이 -80 보다 수신강도가 좋다.

36 채널은 5GHz를 사용한다.
참고 https://en.wikipedia.org/wiki/List_of_WLAN_channels

     agrCtlRSSI: -60
     agrExtRSSI: 0
    agrCtlNoise: -96
    agrExtNoise: 0
          state: running
        op mode: station
     lastTxRate: 540
        maxRate: 600
lastAssocStatus: 0
    802.11 auth: open
      link auth: wpa2
          BSSID: x:xx:xx:xx:xx:xx
           SSID: ysoftman_wifi
            MCS: 0
        channel: 36,1

option 키를 누른상태에서 wifi 아이콘 클릭해 세부정보를 보면
5GHz, 802.11ac, 400Mbps로 정상적으로 보인다.


그런데 network utility.app 으로 wifi 보면 링크 속도가 낮게 보인다.


다음 명령으로 링크속도가 어떻게 설정되어 있는지 확인해보면 자동설정이라고 되어 있다.
ifconfig en0 | grep media
media: autoselect

같은 네트워크에서 갤럭시폰 링크 속도(전화에서 *123456# -> wifi 선택하면 보인다.)는 1Gbps 를 지원하는 802.11ac 네트워크로 실제 400Mbps 이다.


cron 으로 speedtest 매시간 돌려본 결과다.
맥북을 리부팅하면 속도가 잘 나오지만 시간이 갈수록 속도가 떨어진다.
Mon Jan 28 13:00:00 KST 2019 13:00  up  1:53, 3 users, load averages: 3.34 2.87 2.52 Download: 270.22 Mbit/s
Mon Jan 28 14:00:00 KST 2019 14:00  up  2:53, 4 users, load averages: 1.79 1.83 1.80 Download: 102.31 Mbit/s
Mon Jan 28 15:00:00 KST 2019 15:00  up  3:53, 3 users, load averages: 1.41 1.61 1.69 Download: 128.40 Mbit/s
Mon Jan 28 16:00:00 KST 2019 16:00  up  4:53, 4 users, load averages: 2.38 2.01 1.88 Download: 94.41 Mbit/s
... 생략 ...
Thu Feb  7 07:00:00 KST 2019 7:00  up 7 days, 18:01, 3 users, load averages: 1.45 1.57 1.59 Download: 1.77 Mbit/s
Thu Feb  7 08:00:00 KST 2019 8:00  up 7 days, 19:01, 3 users, load averages: 1.59 1.57 1.55 Download: 0.42 Mbit/s

wifi 는 최신 암호화 방식인 WPA2PSK-AES 를 사용해야 한다.
AES 를 뺀 나머지(WEP64, WEP128, TKIP)는 모두 금방 해킹된다.
https://www.bodnara.co.kr/bbs/article.html?num=106786

그리고 TKIP 사용하면 속도도 54Mbps 로 제한된다.
https://www.intel.co.kr/content/www/kr/ko/support/articles/000006697/network-and-i-o/wireless-networking.html

하지만 기업용 WPA2보안은 기본 AES암호화를 사용해 속도는 잘나와야 한다.
나와 같은 문제를 격어 apple 문의한 사람이 있는데, 아직 답이 없다.
https://discussions.apple.com/thread/8562488

wifi explorer lite 앱으로 주파수 상태를 양호한 편으로 큰 문제는 없는것 같다.


[시도1]
아무리 찾아봐도 특별한 원인은 찾을 수 없었는데, 사용자 위치 정보 wifi 설정으로 나아졌다는 글을 보고 설정을 변경해봤다.

참고로 저렇게 설정을 꺼두면 wifi 로 내위치를 확인하지 못한다.

시스템 환경 설정 -> 보안 및 개인정보 보호 -> 위치 서비스 -> 시스템 서비스 -> 세부사항 -> Wi-Fi  네트워크 비활성화 후 wifi 를 끄고 다시 시작한다.


위 설정후 처음에는 속도가 높았지만 점점 속도가 느려서 해결책이 되지 못했다.

[시도2]
다음 위치의 네트웍 관련 설정 파일들을 삭제하고 재부팅한다.
cd /Library/Preferences/SystemConfiguration
sudo rm -f
NetworkInterfaces.plist
com.apple.airport.preferences.plist
com.apple.wifi.message-tracer.plist
preferences.plist

하루정도 지나면 여전히 속도가 느려진다.

[시도3]
sysctl.conf 의 네트워크 관련 설정을 해보자.
https://rolande.wordpress.com/2010/12/30/performance-tuning-the-network-stack-on-mac-osx-10-6/
http://www.cadc-ccda.hia-iha.nrc-cnrc.gc.ca/netperf/tuning-tcp.shtml#osx

현재 커널 tcp 설정 확인해 보면 send/recv 메모리 최대가 2MiB로 너무 작게 설정되어 있다. 참고로 리눅스와 sysctl 변수 이름과 다르다.
sysctl -a | grep -E "nmbclusters|maxsockbuf|tcp.*space|tcp.auto|tcp.win"
kern.ipc.maxsockbuf: 8388608
kern.ipc.nmbclusters: 65536
net.inet.tcp.sendspace: 131072
net.inet.tcp.recvspace: 131072
net.inet.tcp.autorcvbufmax: 2097152
net.inet.tcp.autosndbufinc: 8192
net.inet.tcp.autosndbufmax: 2097152
net.inet.tcp.win_scale_factor: 3

nmbclusters 는 네트워크에 사용되는 버퍼(메모리) 클러스터로 클러스터 1개가 약 2KiB 를 차지한다.

64MiB 를 메모리를 사용하려면 (1024*1024*64)/(1024*2) = 32768 개를 설정한다.

위 설정을 보면 이미 65536 으로 128MiB 로 설정되어 있다.
참고 https://wiki.freebsd.org/SystemTuning

nmbclusters 는 바로 적용할 수 없어 /etc/sysctl.conf 에서 수정해야 한다.
maxsockbuf 는 nmbclusters 이상 넘을 수 없다.

다음과 같이 64MiB 로 늘리려고 하면 다음과 같이 에러가 발생한다.

sudo sysctl -w kern.ipc.maxsockbuf=67108864
sysctl: kern.ipc.maxsockbuf=33554432: Result too large

sendspace, recvspace 은 maxsockbuf 이상 넘을 수 없다.

sudo sysctl -w net.inet.tcp.sendspace=4194304
sudo sysctl -w net.inet.tcp.recvspace=4194304

32MiB=33554432 또는 64MiB=67108864

sudo sysctl -w net.inet.tcp.autorcvbufmax=67108864
sudo sysctl -w net.inet.tcp.autosndbufmax=67108864

win_scale_factor(윈도우 크기 배수)도 최근 8배까지 지원되니 바꿔준다.
sudo sysctl -w net.inet.tcp.win_scale_factor=8

win_scale_factor rfc1323(TCP high performace) 스펙 추가사항이라

다음과 같이 활성화 해야 되지만 mojave 에선 더이상 설정할 수 없는것 같다.
sudo sysctl -w net.inet.tcp.rfc1323=1
sysctl: unknown oid 'net.inet.tcp.rfc1323'

위 내용 정리해서, sysctl -w 은 shutdown 하면 사라지기 때문에 /etc/sysctl.conf 를 수정하자.
kern.ipc.nmbclusters=65536
kern.ipc.maxsockbuf=67108864
net.inet.tcp.sendspace=4194304
net.inet.tcp.recvspace=4194304
net.inet.tcp.autorcvbufmax=67108864
net.inet.tcp.autosndbufmax=67108864
net.inet.tcp.win_scale_factor=8

참고로 netstat -m 으로 네트워크 메모리 사용현황을 파악할 수 있는데, 속도가 100Mbps 정도 나왔을때 다음과 같이 25% 정도를 네트워크에 사용하고 있다.
watch -n 1 'netstat -m'
814/1883 mbufs in use:
812 mbufs allocated to data
2 mbufs allocated to packet tags
1069 mbufs allocated to caches
787/1912 mbuf 2KB clusters in use
0/645 mbuf 4KB clusters in use
0/0 mbuf 16KB clusters in use
7156 KB allocated to network (24.8% in use)
0 KB returned to the system
0 requests for memory denied
0 requests for memory delayed
0 calls to drain routines

하루정도 지나면 여전히 속도가 느려진다.

[시도4]
블루투스가 영향이 있을까 해서 블루투스 비활성화 해봤지만 영향이 없음

[시도5]
wifi 및 시스템 전체 로깅을 실시간으로 볼 수 있다.

커널쪽에서 메시지만 골라 보면
log stream | grep kernel

다음과 같은 메시지가 주기적으로 나타난다.
kernel: (PulseSecureFirewall) PulseSecureDriver - IpPacketCookiesManager.cpp:226 - INFO Deleting expired cookies

pulse app 종료를 해도 계속 나타나  pulse app 을 지웠다.
지금까지는 꼭 재부팅을 해야 네트워크 속도가 올라갔는데, wifi만 끄고 켰는데 속도가 꽤 올라간다.
흠, 몇일 지켜봤는데 속도 저하는 발생하지 않고 있다.
pulse 9.0.2 버전에서 발생했고 이슈를 회사에 전달했고, 문제가 해결된 pulse 9.1 이상을 사용하고 있다.

참고로 pulse 를 완전 제거 후 장시간 맥북을 켜두면 wifi 속도가 현저하게 떨어지지 않지만 2~30mbps 정도로 유지되는 경우 wifi 를 컸다키면 다시 속도가 100mbps 이상으로 올라간다.

golang MaxIdleConnsPerHost 조정하기

# golang 기본 클라이언트 transport 의 기본 설정은
# 한개의 호스트에 Idle Connections(time_wait)를 2개로 제한되어 있다.
# 이를 변경해서 time_wait 를 줄일 수 있다.
# 참고
http://tleyden.github.io/blog/2016/11/21/tuning-the-go-http-client-library-for-load-testing/

# 테스트 내용
https://github.com/ysoftman/test_code/blob/develop/golang/max_idle_conns_per_host/max_idle_conns_per_host.go

kernel time_wait

# 다음 netstat 로 time_wait 보면 PID/Program name 이 - 표시된 케이스가 있다.
netstat -natop | grep -i time_wait
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name     Timer

tcp        0      0 1.1.1.1:12345       2.2.2.2:10000      TIME_WAIT   -                    timewait (20.69/0/0)

# - 는 해당 포트를 사용하던 /proc/$PID 가 사라져(종료) 커널이 관리하게되는 경우다.
# 커널이 잡고 있는 time_wait 포트를 없애기 위해서는 네트워크를 종료하는 방법이 있지만 서비스 중인 서버에서는 사용할 수 없다.
/etc/init.d/network restart

# 드문 경우지만 MSL(Maximum Segment Lifetime, tcp 세그먼트가 네트워크에 존재하는 최대시간으로 보통 2분)이 넘어가서 서버로 부터 fin 을 받지 못할때 발생할 수 있다.
https://en.wikipedia.org/wiki/Maximum_segment_lifetime
# tcp_tw_reuse=1 로 재사용되지 않으면 net.ipv4.tcp_fin_timeout ~ MSL 시간이 지나면 사라진다.

# tcp_fin_timeout 확인
sysctl net.ipv4.tcp_fin_timeout

네트워크 관련 sysctl

# 연결 지향 tcp 는 데이터를 주고 받기전 우선 연결된다.
# 리슨을 위해 바인딩된 포트 열어 놓는 쪽은 passive open으로 서버가 되고,
# 연결을 요청하는 쪽은 active open으로 클라가 된다.
# tcp 종료 과정은 다음과 같다.
# 그림 출처 : 위키피디아 https://en.wikipedia.org/wiki/Transmission_Control_Protocol


# sysctl 은 런타임시에 커널의 파리미터를 설정하는 명령어다.
# /etc/sysctl.conf 파일로도 설정할 수 있다.
# /proc/sys/net/ipv4 에 파일 수정으로 설정할 수 있다.
https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt
http://lxr.linux.no/linux+v3.2.8/Documentation/networking/ip-sysctl.txt
# 이중 net.ipv4 로 시작하는 tcp 제어 설정에서 중요한 몇개를 살펴 보자.

# 로컬 포트가 고갈되는 경우
# TIME_WAIT(fin을 받고 서버에 ack 보내고 일정 시간 기다리는 상태)의 소켓들을 재사용할지 여부
# 0 : 비활성화
# 1 : 활성화 (보통 1로 사용)
# 2 : loopback 에서만 활성화
net.ipv4.tcp_tw_reuse = 1

# FIN_WAIT_2(서버쪽 close 중임을 클라에게 알리는 ack 를 받았을때의 클라 상태)이후
# last_ack 를 받지 못했을때 대기시간으로 디폴트 60초이다.
# 참고로 TIME_WAIT 의 타임아웃은 커널소스에 60초로 하드 코딩되어 있다.
# /usr/src/kernels/3.10.0-957.1.3.el7.x86_64.debug/include/net/tcp.h
# #define TCP_TIMEWAIT_LEN (60*HZ)
net.ipv4.tcp_fin_timeout = 60

# 로컬 포트 사용(허용) 범위
net.ipv4.ip_local_port_range = 23768 60999

# tcp_tw_use 처럼 TIME_WAIT 상태의 소켓을 재사용(recycle)할지 여부
# 디폴트는 비활성화
# 여러 문제가 발생될 소지가 있어 최근 리눅스 4.12 에서는 제거되었다고 한다.
https://vincent.bernat.ch/en/blog/2014-tcp-time-wait-state-linux
net.ipv4.tcp_tw_recycle = 0

Http 기본 설명

Http 를 사용하면 알고 있어야 할 내용들이 아래 링크에 잘 정리된 것 같다.
http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/Network_Programing/AdvancedComm/HTTP

DB 커넥션 끊김 방지하기

보통 DB에서는 쿼리 요청이 없는 Connection 은 일정시간이 지나면 끊어 버린다.

# MySQL 경우 기본 커넥션유지는 interactive_timeout 28800 (8시간)이다.
https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_interactive_timeout
# 각종 타임 아웃 시간 보기
# 대기시간(연결 유지)은 wait_timeout
show variables like '%timeout%';

따라서 일정 주기만 커넥션을 유지를 위한 더미 쿼리(ex "Select 1;")를 요청해주도록 한다.
아니면 각 DB 마다 연결끊김 에러코드를 인지하여 다시 연결하도록 한다.

oracle
ORA-00028: your session has been killed
ORA-01012: not logged on
ORA-01033: ORACLE initialization or shutdown in progress
ORA-01034: ORACLE not available
ORA-01089: immediate shutdown in progress - no operations are permitted
ORA-03113: end-of-file on communication channel
ORA-03114: not connected to ORACLE
ORA-03127: no new operations allowed until the active operation ends
ORA-12571: TNS:packet writer failure

mssql
10054 : Connection Reset By peer (tcp 의 RST 로 비정상 종료로 연결을 끊는 경우)
10061 : Network Error

mysql
2003 : Can't connect to MySQL server
2013 : Lost connection to MySQL server during query
2006 : MySQL server has gone away


IOCP(Input/Output Completion Port)

[IOCP(Input/Output Completion Port) 란?]
- 입력과 출력의 완료를 담당할 포트를 지정해서 처리하겠다는 의미로, 프로그램이 소켓이나 파일 등의 핸들에 대해서 IO 처리를 할 때, 블록되지 않게 하여 프로그램의 대기시간을 줄여주는 방법

[IOCP 흐름]
- 입출력이 완료되면 입출력 완료 대기열(큐)에 쌓인다.
대기열에 쌓이면 워커 쓰레드가 대기열(큐)에 있는 완료보고를 감지하고 사용자 버퍼를 읽어 작업(처리)한다.
[IOCP 장점]
- 처리할 소켓 수 증가에 따른 쓰레드 개수를 최소화
** 클라이언트 연결이 있을때 마다 쓰레드를 생성하는 멀티 쓰레드 방식과 달리 미리 일정한 쓰레드를 만들어 두고 I/O 완료(변화)를  기다릴 수 있음(보통 시스템 cpu 개수 * 2 개의 쓰레드 생성)
** 쓰레드 풀과 비슷해 보이지만 쓰레드 풀 방식에서는 쓰레드에 작업을 할당하기 위해서 개발자가 직접 구현, 하지만 IOCP 는 OS 가 알아서 쓰레드를 선택하여 작업 처리를 해줌

- Select 방식과 비교했을 때
** Select 의 경우 I/O 들의 변화를 감지하기 위해서 I/O 상태를 나타내는 배열을 루프를 돌며 일일이 하나씩 검사해야 하기 때문에, 생성된 I/O 만큼의 시간 지연
**  IOCP 는 작업 쓰레드 내에서 I/O 를 Completion Queue 로 부터 감지하여 처리하기 때문에 따로 변화감지를 위해서 일일이 검사하는 루프를 돌지 않음

- Linux Epoll 방식과 비교했을 때
** Linux epoll 의 경우 커널 영역의 데이터 스트림을 유저영역으로 복사하는 만큼 지연
** IOCP 는 이벤트를 받아 이벤트 루틴이 깨어날 때 이미 데이터 스트림이 유저 영역에 있어 바로 작업 쓰레드로 넘겨 지연 없이 다음 이벤트를 기다릴 수 있음

[IOCP 단점]
- 클라이언트가 적다면 비교적 구현이 간단한 select 방식 이용

[IOCP 를 사용하려면...]
- OS : Windows 2000, 2003, 2008 Server 
- Library : Kernel32.lib (Kernel32.dll)
- Function : CreateIoCompletionPort(), GetQueuedCompletionStatus()
- Winsock2.2 function : WSAStartup(), WSACleanup(), WSASocket(), WSAAccept() etc...

[IOCP 사용 과정]
- IOCP 핸들 생성 및 IOCP 핸들에 소켓 등록
- 소켓에 대한 I/O 작업이 완료되면 큐에 기록되고 쓰레드가 큐를 읽어 작업 수행(IOCP 감시)

[IOCP 핸들 생성 및 IOCP 핸들에 소켓 등록]
- 함수
HANDLE CreateIoCompletionPort(
HANDLE FileHandle,
HANDLE ExistingCompletionPort,
ULONG_PTR CompletionKey, 
DWORD NumberOfConcurrentThreads)

- 파라미터
FileHandle : IOCP에 등록할 파일(or소켓) 핸들
ExistingCompletionPort : 새로운 포트를 생성하려면 NULL, 기존의 IOCP 에 연결하려면, CreateIoCompletionPort()가 이전에 리턴했던 핸들 사용
CompletionKey : 핸들에 대한 작업이 끝나고 알려줄 때 핸들 값 자체를 넘겨주는 것이 아니라, 키 값을 알려준다. 여러 개의 핸들을 등록할 때 키 값을 각각의 핸들마다 고유해야 한다.
NumberOfConcurrentThreads : IOCP 가 입출력 작업할 때 얼마나 많은 쓰레드를 사용할지 설정한다. 가장 최적화된 쓰레드 개수를 지정하려면 0으로 설정한다.

- 사용예
// IOCP 핸들 생성
HANDLE g_hIOCP = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, NULL);
// IOCP 핸들에 소켓 등록
SOCKET client_socket;
int key = 1;
g_hIOCP = ::CreateIoCompletionPort(client_socket, g_hIOCP, key, 0);

[IOCP 감시(쓰레드에서 사용)]
- 함수
BOOL GetQueuedCompletionStatus(
HANDLE CompletionPort,
LPDWORD lpNumberOfBytes,
PULONG_PTR lpCompletionKey,
LPOVERLAPPED* lpOverlapped,
DWORD dwMilliseconds)

- 파라미터
CompletionPort : IOCP 핸들
lpNumberOfBytes : 완료된 I/O 의 총 바이트 수
lpCompletionKey : 핸들간의 구분을 위한 키
lpOverlapped : Overlapped 구조체 주소
dwMilliseconds : 타임아웃 (INFINITE 값을 주면 I/O 완료까지 블록)

- 사용예
// 쓰레드 함수 내에서
Int read;
Int key;
OVERLAPPED *overlapped;
// IOCP 로 완료한 입출력이 있는지 큐에서 확인한다.
GetQueuedCompletionStatus(g_hIOCP, &read, &key, (LPOVERLAPPED*)&overlapped, INFINITE);
HANDLE hIOCP = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, NULL);
// read 를 읽어서 0이 아니라면 작업 수행
If (read == 0) continue;
// do something…

Windows 네트워크 어댑터 우선순위 설정하기

하나의 PC 에서 네트워크를 2개 이상 사용하는 경우(예를 들어 Wifi 와 Lan 등) 어떤 것을 먼저 인식하게 될까?
네트워크 우선순위 : 어댑터 설정 변경 -> alt 로 메뉴 표시 -> 고급 -> 고급설정 -> 연결 항목 우선순위 설정(아래 그림 참고)




VirtualBox 호스트(Windows) 에서 게스트(Linux) 로 ssh 접속하기

호스트가 Windows 일때, VirtualBox 상에서 Linux(CentOS로 설명) 설치 완료 후 ssh 로 접속하기 방법이다.
우선, VirtualBox -> 파일 -> 환경설정 -> 네트워크 -> 호스트 전용 네트워크 에서 어댑터와 DHCP 서버를 아래와 같이 설정한다.




CentOS (power off 상태) 의 네트워크 설정에서 어댑터1과 어댑터2를 아래와 같이 설정한다.
어댑터1 은 게스트(CentOS)의 사설ip 를 호스트 ip에 사상하기 위해 NAT(Network Address Translation) 로 설정하고
어댑터2 는 호스트(Window)에서 게스트 네트워크를 사용할 수 있도록 호스트 전용 어댑터를 설정하도록 한다.




설정을 마치면 아래와 같이 CentOS 에서 어댑터 2개를 사용하는 것으로 나타난다.


CentOS 를 시작하면 eth0 과 eth1 의 두개의 네트워크 어댑터가 동작하는 것을 볼 수 있다.


[첫번째 방법]
CentOS System ->Administration -> Security Level and Firewall -> Other ports ->22 포트 tcp 프로토콜 추가

[두번째 방법]
VirtualBox 가 설치된 곳(예 c:\program files\oracle\virtualbox\)에 VBoxManage.exe 유틸리티를 이용하여 ssh 를 설정한다.

설정 하기, CentOS 5.5 는 virtualbox 에서의 이미지 이름이고 아래와 같이 값을 설정한다.(이미 값이 존재하면 삭제해야함)
VBoxManage setextradata "CentOS 5.5" "VBoxInternal/Devices/pcnet/0/LUN#0/Config/ssh/HostPort" 22
VBoxManage setextradata "CentOS 5.5" "VBoxInternal/Devices/pcnet/0/LUN#0/Config/ssh/GuestPort" 22
VBoxManage setextradata "CentOS 5.5" "VBoxInternal/Devices/pcnet/0/LUN#0/Config/ssh/Protocol" TCP

참고로 설정 지우기, 값을 아무것도 설정하지 않으면 지워진다.
VBoxManage setextradata "CentOS 5.5" "VBoxInternal/Devices/pcnet/0/LUN#0/Config/ssh/HostPort"
VBoxManage setextradata "CentOS 5.5" "VBoxInternal/Devices/pcnet/0/LUN#0/Config/ssh/GuestPort"
VBoxManage setextradata "CentOS 5.5" "VBoxInternal/Devices/pcnet/0/LUN#0/Config/ssh/Protocol"

설정 상황 보기(CentOS 5.5 는 virtualbox 에서의 이미지 이름)
VBoxManage getextradata "CentOS 5.5" enumerate

#####

최신 VirtualBox 에서는 (예 4.0.8) Port Forwarding 을 통해 간단하게 호스트OS(또는 외부)에서 게스트OS 의 ftp, ssh, http 서비스에 접속할 수 있다.
NAT(Network Address Translation)에 포트 포워딩을 사용하여 호스트ip,port 를 게스트ip,port로 포워딩 되도록 하게 하면,
호스트ip,port 로 접속하는 요청은 호스트전용 어댑터ip,port 를 통해 게스트로 접속하게 된다.




Linux IP 및 DNS 설정 변경하기

# IP 변경
# 이더넷 장치에 해당하는 설정 파일 변경
vi /etc/sysconfig/network-script/ifcfg-eth0
HWADDR=xx:xx:xx:xx:xx:xx
IPADDR=xxx.xxx.xxx.xxx
NETMASK=255.255.255.0
GATEWAY=xxx.xxx.xxx.xxx

# DNS 변경
# dns 설정 파일 변경
vi /etc/resolv.conf
nameserver xxx.xxx.xxx.xxx
nameserver xxx.xxx.xxx.xxx

# 변경 내용 적용
/etc/rc.d/init.d/network restart

# DNS 설정 확인
nslookup www.google.com | grep -i server

# 또는
dig www.google.com | grep -i server

Linux 시스템 다양한 정보 보기, 성능 분석

# 시스템 정보 보기 (호스트네임,커널,프로세서,OS,..etc)
uname -a

# linux 이름 및 버전 정보 보기
vi /etc/issue

# 또는
cat /etc/*-release

# 사용자 리스트
vi /etc/passwd

# CPU 정보
vi /proc/cpuinfo

# memory 정보
vi /proc/meminfo

# 또는
# -m MB 로 표시, -g GB 로 표시,...etc
free -m

# 또는
# virtual memory 상태 -w wide
vmstat -w

# 또는
# -s status -S m MB 로 표시
vmstat -s -S m

# vmstat,mpstat,sar 설치
yum install sysstat

# memory 사용률 1초마다 갱신해서 5번까지 보기
vmstat 1 5

# 시스템 성능 통계
# cpu 사용률 1초마다 갱신해서 5번까지 보기
sar 1 5

# process 사용률 1초마다 갱신해서 5번까지 보기
mpstat 1 5

# Disk 별 uuid 정보
ls -ahl /dev/disk/by-uuid/

# Disk 사용량 보기
df -h

# 부팅 후 운용시간 보기
uptime

# dns 서버 설정
vi /etc/resolv.conf

# host 커스텀 설정
vi /etc/hosts

# hostname 보기
cat /proc/sys/kernel/hostname

# 시스템 정보
sudo dmidecode -t system

# 메모리 정보
sudo dmidecode -t memory

# 바이오스 정보
sudo dmidecode -t bios

# 소켓 간단한 상태 보기
cat /proc/net/sockstat

# 소케 메모리 사용량 확인
netstat -m

# 소켓 사용 현황 보기
# -n(numberic ip 로 표현, 도메인 리졸브 안함)
# -a(all)
# -t(tcp)
netstat -nat

# 또는
ss -nat

# 도메인 ip 보기
nslookup google.com

# 또는(좀더 자세하다.)
dig google.com

# 패킷 라우팅 보기
traceroute google.com

# 포트 범위
sysctl net.ipv4.ip_local_port_range

# 또는
cat /proc/sys/net/ipv4/ip_local_port_range

# tcp 관련 설정 정보
sysctl -a | grep net.ipv4.tcp

# 프로세스가 사용하는 시스템 콜 api 보기
sudo yum install strace
strace 명령어

# 예) ls 명령에서 open 시스템 콜만 골라 보기
strace -e trace=open ls

# 예) 실행중인 프레스 attach 해서, 결과 파일로 쓰기
strace -p 프로세스id -o zzz.strace

# 열린 파일 리스트
sudo yum install lsof
lsof

# 1234 포트 사용중인 파일 리스트
# 해당 PID 를 파악할 수 있다.
lsof -i :1234

# rsync 커맨드가 사용중인 파일 리스트
lsof -c rsync

# io 트랙픽 보기
sudo yum install iotop
sudo iotop

# network 트래픽 보기
# -i  eth0 으로 특정 네트워크 인터페이스를 지정할 수 있다.
sudo yum install iftop
iftop

# 시스템 부팅 시 메시지
dmesg -e

# 에러, startup, shutdown 등의 각종 시스템 이벤트 로그
sudo vi /var/log/messages


#####


# mac 참고
# 모든 정보
sysctl -a

# cpu 브랜드 이름
sysctl -a | grep cpu.brand_string

# cpu 개수
sysctl -n hw.ncpu

# 메모리 크기
sysctl -n hw.memsize

# 메모리 사용량 1초마다 보기
vm_stat  1

# 컴퓨터 이름 보기
networksetup -getcomputername

# 컴퓨터 이름 설정
networksetup -setcomputername "ysoftman macbook pro"

# 사용 가능한 네트워크 종류 보기
networksetup -listallnetworkservices

# 연결중인 네트워크 리스트
networksetup -listallhardwareports

# wi-fi(listallnetworkservices 중하나) 정보
networksetup -getinfo wi-fi

# en0(보통 wi-fi) 끄기
networksetup -setairportpower en0 off

# en0(보통 wi-fi) 켜기
networksetup -setairportpower en0 on

# airport(wi-fi) 상태 정보
/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I

# 시스템 자세한 정보
system_profiler

# 시스템 자세한 정보(app)
spotlight -> 시스템 정보.app(또는 system profiler 로 실행)

# 시스템 메시지(로그)
# mac 에선 dmesg 로 부팅 로그 전체를 볼 수 없어 log 를 사용하는것이 좋다.
# log 는 dmesg 를 포함한 시스템 로그를 취합해 보여준다.
# start 날짜 이후의 시스템 메시지
log show --start "2010-02-19"

# 실시간 시스템 로그 보기
log stream

# 로그 전체 삭제
sudo log erase --all

# 시스템 로그 파일
vi /var/log/system.log

# 참고 리눅스에서 상황/대상별에 맞는 분석/성능 툴
http://www.joyent.com/blog/linux-performance-analysis-and-tools-brendan-gregg-s-talk-at-scale-11x