TCP 3-way handshake

Cute_Security15·2023년 7월 30일
1

커널

목록 보기
1/10
post-thumbnail

상황

ncat server 에 커널 모듈을 올리고, 특정 ip 에서 오는 recvmsg 이벤트는 차단시키는 중

구체적으로는
1) lsm 에서 제공하는 security_socket_recvmsg 에 콜백함수를 등록
2) 커널모듈이 recvmsg 콜백을 받으면, 차단하고 싶은 ip (foreign ip) 인지 확인
3) 확인하는 방법은 "함수 인자로 넘겨받은 sk" 를 써서 inet socket 을 획득하고 saddr 체크

문제는 saddr 엔 자신의 ip (local ip) 가 적혀있고, daddr 에 차단하고 싶은 ip 가 적혀있음

목적

recvmsg 이벤트에서 차단하려는 ip 가 어떻게 sk daddr 에 적히는지 확인하기 위해,
TCP 3-way handshake 과정을 따라가봤습니다.

새로 알게된 부분

recvmsg 이벤트에서 쓰는 sk 는 단순히 서버 소켓이 아니라,
listener sk, request_sock, child sk 가 참여해서 만들어진 것이고,
이 과정에 inet socket hashtable, listener sk acccept queue 등이 참여하는 걸 확인

TCP 연결 순서도 with 커널코드

perf-tool (+crash) 로 커널 콜스택을 먼저 확인하고, 이후 함수인자와 리턴값을 체크하면서
코드 흐름을 따라갔고, 확인된 생각들을 모아 정리하였습니다.

server 가 SYN 패킷수신

root@ubuntu22-virtual-machine bin (master) $ ./kprobe -s 'p:tcp_v4_send_synack'
Tracing kprobe tcp_v4_send_synack. Ctrl-C to end.
-0 [003] ..s.. 189346.050282: tcp_v4_send_synack: (tcp_v4_send_synack+0x0/0x200)
-0 [003] ..s.. 189346.050304:
=> tcp_v4_send_synack // 3. SYN-ACK 전송
=> tcp_v4_conn_request // 2. 생성, request_sock 에 SYN 패킷정보 작성, inet establish hashtable 에 request_sock 저장, listener sk 의 accept queue 에 request_sock 추가
=> tcp_rcv_state_process // 1. TCP 상태에 따라 패킷처리
=> tcp_v4_do_rcv
=> tcp_v4_rcv
=> ip_protocol_deliver_rcu
=> ip_local_deliver_finish
=> ip_local_deliver
=> ip_sublist_rcv_finish
=> ip_sublist_rcv
=> ip_list_rcv
=> netif_receive_skb_list_core
=> netif_receive_skb_list_internal
=> napi_complete_done
=> e1000_clean
=>
napi_poll
=> net_rx_action
=> __do_softirq
=> irq_exit_rcu
=> common_interrupt
=> asm_common_interrupt
=> native_safe_halt
=> acpi_idle_enter
=> cpuidle_enter_state
=> cpuidle_enter
=> cpuidle_idle_call
=> do_idle
=> cpu_startup_entry
=> start_secondary
=> secondary_startup_64_no_verify

SERVER 가 ACK 패킷수신

root@ubuntu22-virtual-machine bin (master) $ ./kprobe -s 'p:inet_csk_complete_hashdance'
Tracing kprobe inet_csk_complete_hashdance. Ctrl-C to end.
-0 [003] ..s.. 189680.562646: inet_csk_complete_hashdance: (inet_csk_complete_hashdance+0x0/0x320)
-0 [003] ..s.. 189680.562667:
=> inet_csk_complete_hashdance // 5. listener sk 의 accept queue 에서 request 소켓을 제거, 그리고 child 소켓을 추가
=> tcp_v4_rcv // 4. (tcp_check_req, tcp_v4_syn_recv_sock) child 소켓 생성후 request_sock 의 정보를 기록 (inet_ehash_nolisten) inet establish hashtable 에 child 소켓 저장
=> ip_protocol_deliver_rcu
=> ip_local_deliver_finish
=> ip_local_deliver
=> ip_sublist_rcv_finish
=> ip_sublist_rcv
=> ip_list_rcv
=> netif_receive_skb_list_core
=> netif_receive_skb_list_internal
=> napi_complete_done
=> e1000_clean
=>
napi_poll
=> net_rx_action
=> __do_softirq
=> irq_exit_rcu
=> common_interrupt
=> asm_common_interrupt
=> native_safe_halt
=> acpi_idle_enter
=> cpuidle_enter_state
=> cpuidle_enter
=> cpuidle_idle_call
=> do_idle
=> cpu_startup_entry
=> start_secondary
=> secondary_startup_64_no_verify

SERVER 가 accept 시스템콜 호출

root@ubuntu22-virtual-machine bin (master) $ ./kprobe -s 'r:inet_csk_accept'
Tracing kprobe inet_csk_accept. Ctrl-C to end.
ncat-108379 [001] ..... 190208.782859: inet_csk_accept: (inet_accept+0x42/0x170 <- inet_csk_accept)
ncat-108379 [001] ..... 190208.782869:
=> [unknown/kretprobe'd] // 6. (inet_csk_accept) listener sk 의 accept queue 에서 child 소켓을 획득 (inet_accept) child 소켓을 file_struct.private_data.socket.sk 에 저장
=> do_accept
=> sys_accept4_file
=>
sys_accept4
=> __x64_sys_accept
=> do_syscall_64
=> entry_SYSCALL_64_after_hwframe

질문 1

tcp 3-way handshake 로 생성된 child sk 는 어디에 등록되는가?
--> listener sk 의 accept queue 에 등록됩니다.

질문 2

child sk 는 recvmsg 시스템콜에서 쓰는 sk 와 동일한가?
--> 동일합니다. accept 시스템콜 시, 프로세스가 열어둔 socket 의 sk 로 들어갑니다. (sock_graft 함수 참고)

child sk 는 0xffff888007a4e040

root@ubuntu22-virtual-machine bin (master) $ ./kprobe -s 'r:inet_csk_complete_hashdance child=$retval'
Tracing kprobe inet_csk_complete_hashdance. Ctrl-C to end.
-0 [003] ..s.. 186587.749344: inet_csk_complete_hashdance: (tcp_check_req+0x1ae/0x620 <- inet_csk_complete_hashdance) child=0xffff888007a4e040
-0 [003] ..s.. 186587.749369:
=> [unknown/kretprobe'd]
=> tcp_v4_rcv
=> ip_protocol_deliver_rcu
=> ip_local_deliver_finish
=> ip_local_deliver
=> ip_sublist_rcv_finish
=> ip_sublist_rcv
=> ip_list_rcv
=> netif_receive_skb_list_core
=> netif_receive_skb_list_internal
=> napi_complete_done
=> e1000_clean
=>
napi_poll
=> net_rx_action
=> __do_softirq
=> irq_exit_rcu
=> common_interrupt
=> asm_common_interrupt
=> native_safe_halt
=> acpi_idle_enter
=> cpuidle_enter_state
=> cpuidle_enter
=> cpuidle_idle_call
=> do_idle
=> cpu_startup_entry
=> start_secondary
=> secondary_startup_64_no_verify

recvmsg 시스템콜에서 쓰는 sk 도 0xffff888007a4e040 로 일치

crash> ps | grep ncat
106445 26003 1 ffff88800e00ca40 IN 0.2 13160 4652 ncat
crash> files 106445
PID: 106445 TASK: ffff88800e00ca40 CPU: 1 COMMAND: "ncat"
ROOT: / CWD: /root/lup/target/debug
FD FILE DENTRY INODE TYPE PATH
0 ffff888011179f00 ffff8880316b6240 ffff88801236ef80 CHR /dev/pts/2
1 ffff888011179f00 ffff8880316b6240 ffff88801236ef80 CHR /dev/pts/2
2 ffff888011179f00 ffff8880316b6240 ffff88801236ef80 CHR /dev/pts/2
5 ffff88800ebfa500 ffff8880076a29c0 ffff888031558700 SOCK TCP

crash> struct file.private_data ffff88800ebfa500
private_data = 0xffff888031558680,
crash> struct socket.sk 0xffff888031558680
sk = 0xffff888007a4e040,
crash>

profile
관심분야 : Filesystem, Data structure, user/kernel IPC

1개의 댓글

comment-user-thumbnail
2023년 8월 5일

TCP 헤더정보 정리 + tcpdump 로 확인 : https://evan-moon.github.io/2019/11/10/header-of-tcp/

답글 달기