루프백과 127.0.0.1


인터페이스

인터페이스 = 컴퓨터가 네트워크와 통신하는 “창구”. 보통 물리 NIC 에 대응한다 (eth0, ens33). 꼭 물리적일 필요는 없다.

루프백 (loopback)

루프백 = 물리 하드웨어가 없는 가상 인터페이스. 리눅스 이름 lo, IP 127.0.0.1 (IPv6 ::1).

  • 이 인터페이스로 보낸 패킷은 네트워크로 나가지 않고 자기 자신에게 돌아온다. 케이블·스위치·NIC 를 거치지 않고 커널 안에서 U턴.
  • 용도 = 같은 컴퓨터의 두 프로그램이 네트워크 방식으로 통신 (예: 웹 서버 → 127.0.0.1:5432 DB).
  • 대역 = 127.0.0.0/8 전체(127.0.0.1 ~ 127.255.255.255). 관습적으로 127.0.0.1 만 쓴다.

localhost vs 127.0.0.1

  • 127.0.0.1 = 실제 IP 주소. localhost = 그 주소에 붙은 이름.
  • /etc/hosts 가 둘을 연결한다.
127.0.0.1   localhost
::1         localhost
  • localhost 는 이름이라 resolve 과정을 거친다 → /etc/hosts 수정 시 다른 곳을 가리킬 수도.
  • localhost 가 IPv4(127.0.0.1) 와 IPv6(::1) 둘 다에 매핑된다 → 프로그램에 따라 IPv6 로 붙기도 한다.
  • 127.0.0.1 로는 되는데 localhost 로는 안 됨” 의 흔한 원인 = IPv4/IPv6 차이.

패킷이 NIC 없이 U턴하는 과정

  1. 앱이 127.0.0.1 로 송신 → 소켓 → 커널 네트워크 스택.
  2. 커널 라우팅 = “목적지 127.0.0.1lo 담당”.
  3. 패킷이 lo 의 송신 경로로.
  4. lo 는 가상이라 내보낼 하드웨어가 없다 → 송신 패킷을 곧바로 자신의 수신 경로로 재투입.
  5. lo 의 수신 경로 → 커널 스택 → 목적지 소켓의 수신 버퍼.
  • U턴 지점 = lo 의 송신·수신을 잇는 커널 네트워크 스택 내부.
  • 물리 계층(전기신호·케이블·NIC) 은 통째로 건너뛴다.
  • 단, TCP/IP 처리(시퀀스 번호·체크섬) 는 그대로 거친다.

층위 비교

  • 루프백 = 물리 계층 바이패스.
  • DPDK 등 커널 바이패스 = 커널 네트워크 스택 자체 바이패스. 한 단계 더.

바인딩 — 어느 인터페이스에서 받을지

바인딩 = 서버가 listen 할 때 “어느 인터페이스에서 연결을 받을지” 정하는 것.

바인딩 주소동작
127.0.0.1:8080루프백 전용. 같은 컴퓨터 내 연결만. 외부 접속 불가.
0.0.0.0:8080모든 인터페이스. 외부 접속 가능.
전형적 트러블 = “포트가 listen 중인데 외부에서 접속 안 됨” → ss -tulpn 으로 바인딩 주소 확인 → 127.0.0.1:8080 이면 루프백에만 묶인 것 → 0.0.0.0 으로 변경.

IP 확인


  • 인터페이스 IP = 그 인터페이스가 가진 주소. 호스트 자신이 본 모습. NAT 뒤라면 사설 IP. ip addr / ifconfig 로 봄.
  • 외부 (public) IP = 인터넷에서 본 내 IP. NAT 라우터·VPN 의 출구 IP. 호스트 자신은 직접 모른다 → 외부 서비스에 물어봐야 함.
알고 싶은 것도구
내 인터페이스 IPip addr, ifconfig, hostname -I
외부에서 본 내 IPcurl ifconfig.co, curl ifconfig.me, curl ipinfo.io/ip

ifconfig 출력 해부

root@lima-default:~# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.5.15  netmask 255.255.255.0  broadcast 192.168.5.255
        inet6 fe80::5055:55ff:fe3d:a9b1  prefixlen 64  scopeid 0x20<link>
        ether 52:55:55:3d:a9:b1  txqueuelen 1000  (Ethernet)
        RX packets 21647  bytes 29662336 (29.6 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 2818  bytes 215856 (215.8 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
 
lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 95  bytes 7812 (7.8 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 95  bytes 7812 (7.8 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

필드별:

  • eth0 = 인터페이스 이름. 관례 = lo 루프백, eth*/ens*/enp* 이더넷, wlan* WiFi, docker0 도커 브리지, br-* 브리지, tun*/tap* VPN.
  • flags=4163<UP,BROADCAST,RUNNING,MULTICAST> = 인터페이스 상태 비트마스크.
    • UP = admin 활성화 (ip link set eth0 up 으로 켬).
    • RUNNING = 매체(케이블·반송파) 연결됨. UP 인데 RUNNING 아니면 케이블 빠진 상태.
    • BROADCAST / MULTICAST = 해당 트래픽 지원.
  • mtu 1500 = Maximum Transmission Unit. 한 패킷의 최대 바이트(헤더 포함). 이더넷 기본 1500. 루프백(lo) 은 65536 — 가상이라 크게 잡음.
  • inet 192.168.5.15 = IPv4 주소. 192.168.0.0/16 은 RFC 1918 사설 대역 → 인터넷에서 안 보임. (사설 대역 = 10/8, 172.16/12, 192.168/16.)
  • netmask 255.255.255.0 = 같은 서브넷 판정 마스크. CIDR /24 와 같음.
  • broadcast 192.168.5.255 = 그 서브넷의 브로드캐스트 주소.
  • inet6 fe80::5055:55ff:fe3d:a9b1 = IPv6 link-local 주소. fe80::/10 대역, 같은 링크 안에서만 유효. MAC 에서 자동 생성됨.
  • prefixlen 64 = IPv6 prefix 길이 (/64).
  • scopeid 0x20<link> = link-local 스코프.
  • ether 52:55:55:3d:a9:b1 = MAC 주소. (52:55:55 는 lima/QEMU 가상 NIC 의 가짜 OUI.)
  • txqueuelen 1000 = 송신 큐 길이.
  • RX/TX packets/bytes/errors/dropped = 부팅 이후 누적 통계. errors·dropped 가 계속 늘면 NIC·드라이버·케이블 문제 의심.

lo = 루프백 인터페이스. 127.0.0.1 만 가짐. 위 ## 루프백과 127.0.0.1 에서 더.

curl ifconfig.co — 외부 IP 확인 원리

root@lima-default:~# curl ifconfig.co
192.0.2.20

동작 = 단순한 HTTP GET. 서버가 받은 TCP 커넥션의 source IP 를 응답 본문에 echo 해주는 서비스다.

  • 내 호스트 → (NAT 라우터·게이트웨이) → 인터넷 → ifconfig.co 서버
  • 서버가 받는 패킷의 src IP = NAT 의 외부 IP (내 인터페이스 IP 아님)
  • 그 IP 를 그대로 본문으로 돌려줌 → 도구 ifconfig 와 도메인 ifconfig.co 는 이름만 우연히 비슷. 별 관계 없음.

대체 서비스:

curl ifconfig.me
curl icanhazip.com
curl ipinfo.io/ip
curl https://api.ipify.org
 
# DNS 기반 (HTTP 안 거치는 정통파)
dig +short myip.opendns.com @resolver1.opendns.com

최신 도구 = ip (iproute2)

ifconfignet-tools 의 옛 도구로 deprecated. 현대 배포판은 iproute2ip 사용. (왜 갈라졌는지는 아래 ## 네트워크 진단 도구 참고.)

ip addr            # = ip a, 모든 인터페이스의 IP
ip addr show eth0  # 특정 인터페이스만
ip -4 addr         # IPv4 만
ip -6 addr         # IPv6 만
ip -br addr        # brief: 한 줄씩 요약
ip link            # L2 (MAC, MTU, 상태)
ip route           # 라우팅 테이블 — default via ... = 외부 게이트웨이
ip neigh           # ARP 테이블
 
hostname -I        # 내 IP 만 공백 구분 한 줄
hostname -i        # /etc/hosts 기반 (보통 127.0.1.1, 신뢰 X)

스크립트에서 외부 IP 잡기:

IP=$(curl -s ifconfig.co)

Lima 실습

lima-default 안에서:

# 같은 인터페이스를 두 도구로
root@lima-default:~# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.5.15  netmask 255.255.255.0  broadcast 192.168.5.255
        inet6 fe80::5055:55ff:fe3d:a9b1  prefixlen 64  scopeid 0x20<link>
        ether 52:55:55:3d:a9:b1  txqueuelen 1000  (Ethernet)
        RX packets 320070  bytes 456515445 (456.5 MB)
        ...
 
lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        ...
 
root@lima-default:~# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host noprefixroute
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:55:55:3d:a9:b1 brd ff:ff:ff:ff:ff:ff
    altname enx5255553da9b1
    inet 192.168.5.15/24 metric 200 brd 192.168.5.255 scope global dynamic eth0
       valid_lft 2367sec preferred_lft 2367sec
    inet6 fe80::5055:55ff:fe3d:a9b1/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever
  • 같은 인터페이스를 두 포맷으로 — eth0 = 192.168.5.15/24, lo = 127.0.0.1/8.
  • ip addr 가 더 풍부 = CIDR(/24), DHCP 리스 잔여 시간(valid_lft 2367sec), qdisc(큐 정책 fq_codel), altname.
# Brief / 한 줄
root@lima-default:~# hostname -I
192.168.5.15
 
root@lima-default:~# ip -br addr
lo               UNKNOWN        127.0.0.1/8 ::1/128
eth0             UP             192.168.5.15/24 metric 200 fe80::5055:55ff:fe3d:a9b1/64
 
# 특정 인터페이스만
root@lima-default:~# ip addr show lo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host noprefixroute
# 외부 IP
root@lima-default:~# curl ifconfig.co
198.51.100.92
# 라우팅 — default 게이트웨이 확인
root@lima-default:~# ip route
default via 192.168.5.2 dev eth0 proto dhcp src 192.168.5.15 metric 200
192.168.5.0/24 dev eth0 proto kernel scope link src 192.168.5.15 metric 200
192.168.5.2 dev eth0 proto dhcp scope link src 192.168.5.15 metric 200
  • default via 192.168.5.2 dev eth0 = “기본 게이트웨이 = 192.168.5.2, eth0 로 나감”. 외부 향하는 트래픽은 전부 이 IP 로 보냄.
  • 192.168.5.0/24 dev eth0 proto kernel scope link = “같은 서브넷은 직접 도달” — 커널이 인터페이스에 IP 할당하면서 자동 추가.
  • 192.168.5.2 = Lima vmnet 의 NAT 게이트웨이 (실제로는 맥 호스트가 wrapping).
# (옵션) tcpdump 로 curl 패킷 직접 보기
sudo apt-get install -y tcpdump
sudo tcpdump -n -i eth0 'host ifconfig.co' &
curl -s ifconfig.co
# eth0 송신 패킷의 src IP = VM 의 사설 IP (192.168.5.15)
# 응답 본문엔 NAT 통과 후 외부 IP (198.51.100.92). 두 값이 다른 게 핵심.

호스트(맥) 와 비교 — 활성 인터페이스만 추려서:

$ ifconfig
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
        inet 127.0.0.1 netmask 0xff000000
        inet6 ::1 prefixlen 128
        inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1
...
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
        ether aa:bb:cc:00:11:22
        inet 192.168.0.91 netmask 0xffffff00 broadcast 192.168.0.255
        media: autoselect
        status: active
...
utun4: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1420
        inet 10.5.0.2 --> 10.5.0.2 netmask 0xffff0000
 
# 그 외 anpi*·en1~6·bridge0·awdl0·llw0·utun0~6·ap1·gif0·stf0 = 대부분 inactive
# 또는 시스템 가상 인터페이스 (애플 NIC 슬롯·AWDL·LLW 등). 신경 쓸 필요 없음.
 
$ curl ifconfig.co
198.51.100.92
  • lo0 = 127.0.0.1 — 맥의 루프백 (이름이 lo0, 리눅스의 lo 와 다름).
  • en0 = 192.168.0.91 — 활성 Wi-Fi. 맥의 실제 LAN IP.
  • utun4 = 10.5.0.2 — VPN 터널 (사용 중인 VPN 클라이언트가 만든 P2P 가상 NIC).
  • 맥은 ifconfig 가 기본. ip addr 쓰려면 brew install iproute2mac.

외부 IP 가 둘 다 198.51.100.92 — VPN 풀 터널

VM 과 맥의 사설 IP 는 다르지만 외부 IP 는 같다.

위치사설 IP외부 IP (VPN ON)
Lima VM192.168.5.15 (eth0)198.51.100.92
맥 호스트192.168.0.91 (en0)198.51.100.92

처음엔 “두 호스트가 같은 집 공유기 NAT 를 거쳐 같다” 로 보였지만, whois 로 확인하니 다른 답. (아래 출력은 실제 결과를 익명화한 형태)

VPN 켰을 때 — 198.51.100.92

$ whois 198.51.100.92
inetnum:        198.51.100.0 - 198.51.100.255
netname:        EXAMPLE-VPN-HOST
descr:          Example VPN Hosting Inc.
country:        XX
...
route:          198.51.100.0/24
origin:         AS65001
  • Example VPN Hosting 류 = VPN·프록시 호스팅 인프라로 알려진 사업자 부류 (NordVPN·ExpressVPN 같은 사업자들의 exit 노드가 이런 ASN 에 몰려 있음).
  • 등록상 country 와 실제 운영 주체 국가가 다른 경우가 흔함 — VPN 사업자가 여러 지역에 exit POP 을 둔다는 의미.
  • 즉 집 공유기 WAN IP 가 아니라 VPN 서버의 출구 IP.

VPN 끈 뒤 — 203.0.113.134

$ curl ifconfig.co
203.0.113.134

$ whois 203.0.113.134
inetnum:        203.0.113.0 - 203.0.113.255
netname:        EXAMPLE-ISP-NET
descr:          Example Telecom Ltd. Consumer Internet
country:        XX
...
route:          203.0.113.0/24
origin:         AS65002
descr:          Example ISP Ltd.
  • 가정용 ISP (사용 중인 통신사) — owner / ASN 이 위와 완전히 다름.
  • 이게 실제 집 공유기 WAN IP.

실제 흐름 (VPN ON)

Lima VM (192.168.5.15)
  ↓  Lima vmnet NAT (gateway 192.168.5.2 — 맥이 wrapping)
맥 (en0 = 192.168.0.91)
  ↓  Mac VPN 클라이언트가 default route 를 utun4 로 가로챔
utun4 (10.5.0.2)
  ↓  VPN 서버
인터넷 (198.51.100.92, VPN 사업자 출구)

흐름 (VPN OFF)

Lima VM → Mac → 집 공유기 NAT → 인터넷 (203.0.113.134, ISP WAN)

→ VM 과 맥의 외부 IP 가 둘 다 198.51.100.92 였던 진짜 이유 = Mac 의 VPN 이 full-tunnel (default route 통째로 VPN 으로) 이라 Lima 의 outbound 도 같은 터널을 탐. 두 호스트가 같은 NAT 경계가 아니라 같은 VPN 경계 안에 있는 셈.

교훈

  • 외부 IP 가 같다 = 무조건 같은 NAT 경계가 아니라, “같은 마지막 출구” 라는 뜻일 뿐.
  • 출구 정체를 알려면 = whois / curl ipinfo.io/<ip> 로 IP 의 owner·country·ASN 까지 확인.
  • VPN ON/OFF 토글로 비교하면 실제 라우팅 차이를 즉시 확인 가능.

네트워크 진단 도구


“네트워크” 는 여러 층위라 도구마다 담당이 다르다.

담당net-tools (옛)iproute2 (현대)
소켓·커넥션netstatss
라우팅 테이블netstat -r / routeip route
인터페이스 통계netstat -iip -s link
인터페이스 IPifconfigip addr
라우팅 설정route add ...ip route add ...
netstatifconfig 는 라이벌이 아니라 애초에 다른 일을 하는 도구다.

netstat (network statistics)

root@lima-default:~# netstat -lntp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 127.0.0.54:53           0.0.0.0:*               LISTEN      1423/systemd-resolv
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      1423/systemd-resolv
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1/init
tcp        0      0 127.0.0.1:34479         0.0.0.0:*               LISTEN      4286/containerd
tcp6       0      0 :::22                   :::*                    LISTEN      1/init
  • 시스템 네트워크 상태 조회 = 열린 커넥션, listen 포트, 라우팅 등.
  • 내 서버, DB 커넥션 등을 확인할 때 즉, 특정 포트가 잘 떠있는지 확인할 때 사용한다.
  • 대표 관용구 = netstat -tulpn
    • t TCP / u UDP / l listen 중인 소켓만 / p 프로세스·PID / n 숫자 표시(DNS 조회 생략)
  • 오래된 도구. 요즘 배포판은 기본 미설치인 경우가 많다 (net-tools 패키지).

netstat → ss 인데 netstat -r → ip route 인가

  • netstat = 본업(소켓 보기) + -r(라우팅) + -i(인터페이스 통계) 까지 겸직.
  • 현대 iproute2 = 담당별로 재정리.
    • 소켓·커넥션 → ss
    • 라우팅 → ip route
    • 인터페이스·IP → ip addr, ip link
  • 즉 옛 netstat 의 기능이 후속에서 둘로 쪼개진 것. ss -r 이 없는 이유 = 라우팅이 애초에 ss 의 담당이 아니라서.

ss (socket statistics) — 현대 표준

  • netstat 의 후속. 더 빠르고 커널 정보를 더 직접 가져온다.
  • 매핑은 거의 1:1 = netstat -tulpnss -tulpn, netstat -tanss -tan.

netstat -an vs ss -an — 다 보기

-a = all (listen + established + 기타), -n = 숫자 표시 (DNS 안 거침). 옵션 조합이 흔한 관용구라 같이 묶음.

netstat -an = TCP/UDP/RAW + UNIX domain sockets 까지 한 번에. listening + established 다.

root@lima-default:~# netstat -an
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 127.0.0.1:40363         0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN
tcp6       0      0 :::22                   :::*                    LISTEN
udp        0      0 192.168.5.15:68         0.0.0.0:*
raw6       0      0 :::58                   :::*                    7
Active UNIX domain sockets (servers and established)
Proto RefCnt Flags       Type       State         I-Node   Path
unix  2      [ ACC ]     STREAM     LISTENING     2745     /run/systemd/journal/io.systemd.journal
unix  2      [ ACC ]     STREAM     LISTENING     12816    /run/containerd/containerd.sock
... (수십~수백 줄의 unix socket)
  • netstat -an = “이 시스템에 무슨 네트워크 활동 있나” 한 번에 스캔하는 용도.
  • Unix socket 까지 다 같이 나옴 — 단점이자 장점.
  • 단, -p 없으니 PID 없음. 필요하면 netstat -anp.

ss -an = 같은 의도지만 출력 결이 다름. 더 raw — netlink(nl), packet(p_raw), vsock(v_str), mptcp 같은 잘 안 쓰이는 namespace 도 다 노출.

root@lima-default:~# ss -an
RTNETLINK answers: Invalid argument
Netid  State    Recv-Q  Send-Q  Local Address:Port    Peer Address:Port
nl     UNCONN   0       0       0:1                   *
nl     UNCONN   4352    0       4:14409               *
p_raw  UNCONN   0       0       [35020]:eth0          *
u_str  ESTAB    0       0       * 10126               * 10127
u_str  LISTEN   0       4096    /run/systemd/journal/io.systemd.journal 2745  * 0
udp    UNCONN   0       0       127.0.0.54:53         0.0.0.0:*
tcp    LISTEN   0       4096    127.0.0.1:40363       0.0.0.0:*
tcp    LISTEN   0       4096    0.0.0.0:22            0.0.0.0:*
v_str  LISTEN   0       0       *:22                  *:*
v_str  ESTAB    0       0       3:22                  2:492025021
mptcp  LISTEN   0       4096    127.0.0.1:40363       0.0.0.0:*
  • Netid 컬럼이 도구의 통일성 — tcp, udp, nl (netlink), u_str (unix stream), u_dgr (unix dgram), u_seq (unix seqpacket), v_str (vsock = Lima ↔ host 같은 hypervisor 채널), p_raw (raw packet), mptcp (multipath TCP).
  • Send-Q4096 = listen backlog (accept queue 크기 한도). netstat 출력엔 안 나오는 정보.
  • 첫 줄 RTNETLINK answers: Invalid argument = netlink 일부 조회 실패. 무해.
  • v_str 항목은 Lima 같은 VM 환경에서만 보임 (vsock = VM ↔ host 직접 통신).

언제 뭘 쓰나

  • 일상 점검 = ss -tulpn (TCP/UDP listening + PID). -an 보다 노이즈 적음.
  • 시스템 전체 스캔 = netstat -an 또는 ss -an. unix socket 까지 보고 싶을 때.
  • 디버깅 = ss -tnp state established 같은 조건 필터. ss 가 더 강력.

트러블슈팅 활용

  • “특정 포트 연결 안 됨” → ss -tulpn 으로 실제 listen 중인지 + 바인딩 주소 확인.
  • “커넥션이 많이 쌓임” → 상태별로 카운트.
    • TIME_WAIT 다수 = 커넥션을 짧게 자주 맺고 끊는 패턴.
    • CLOSE_WAIT 다수 = 앱이 소켓을 안 닫음 (앱 버그 신호).
  • “이 포트 누가 쓰나” → -p 옵션으로 프로세스·PID.

패킷 버퍼 — 커널 vs 유저 공간

커널 쪽 (소켓 버퍼) = OS 가 소켓마다 들고 있는 송수신 버퍼.

  • 수신 = 데이터 도착 → 앱이 read() 로 가져갈 때까지 커널 수신 버퍼에 적재.
  • 송신 = 앱이 write() 한 데이터 → 실제 전송될 때까지 커널 송신 버퍼에 적재. 확인 도구
  • ss -tm (-m 은 메모리 정보)
    • Recv-Q = 커널 수신 버퍼에 쌓였으나 앱이 아직 안 읽은 데이터. 계속 크면 “앱이 처리를 못 따라감”.
    • Send-Q = 보냈으나 상대가 ACK 안 한 데이터. 크면 네트워크·상대 쪽 문제.
  • sysctl net.ipv4.tcp_rmem / tcp_wmem = 버퍼 크기 기본값·한계.
  • netstat -s / nstat = 시스템 전체 통계, 버퍼 부족 드롭 카운터 등. 유저 공간 쪽 = 앱이 자기 메모리(힙) 에 들고 있는 버퍼.
  • OS 가 들여다보는 표준 도구가 없다 → 그냥 그 프로세스의 일반 메모리.
  • 확인하려면 = 앱 자체 메트릭·로그, 또는 디버거·프로파일러.
  • 프로세스 전체 메모리는 top / ps / /proc/<pid>/status 로 보지만 “그중 버퍼가 얼마” 는 OS 가 구분 못한다. 핵심 구분 = 커널 버퍼는 OS 관리 → ss·sysctl·netstat -s 로 조회 가능. 유저 공간 버퍼는 앱 관리 → OS 도구로는 안 보임.

nslookup — DNS 조회

root@lima-default:~# nslookup google.com
Server:		127.0.0.53
Address:	127.0.0.53#53
 
Non-authoritative answer:
Name:	google.com
Address: 142.250.197.14
 
root@lima-default:~# cat /etc/hosts
127.0.0.1 localhost
  • 도메인 → IP. Server 는 응답한 resolver, Non-authoritative answer 는 캐시·재귀 응답.
  • 현대 권장 = dig (더 풍부한 출력). 예) dig +short google.com.

telnet — 포트 listen 확인

root@lima-default:~# telnet google.com 80
Trying 142.250.197.14...
Connected to google.com.
Escape character is '^]'.
^C
Connection closed by foreign host.
  • L4 에서 특정 포트가 열려있고 서비스가 listening 중인지 확인. TCP 3-way handshake 를 수행한다.
  • ping 과는 다르다. ping 은 L3 에서 호스트가 살아있는지만 확인. ICMP Echo Request/Reply 로 동작하고 포트·TCP/UDP 개념은 포함하지 않는다. ping 응답이 안 오면 네트워크 경로·ICMP 차단·호스트 다운 등을 의심.
  • 현대 대체 = nc -zv host port (netcat). telnet 보다 깔끔.

curl -v — HTTPS 핸드셰이크 보기

root@lima-default:~# curl -v https://google.com
* Host google.com:443 was resolved.
* IPv6: (none)
* IPv4: 142.250.197.14
*   Trying 142.250.197.14:443...
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: /etc/ssl/certs
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519MLKEM768 / id-ecPublicKey
* ALPN: server accepted h2
* Server certificate:
*  subject: CN=*.google.com
*  start date: Dec  3 15:50:09 2025 GMT
*  expire date: Feb 25 15:50:08 2026 GMT
*  subjectAltName: host "google.com" matched cert's "google.com"
*  issuer: C=US; O=Google Trust Services; CN=WE2
*  SSL certificate verify ok.
*   Certificate level 0: Public key type EC/prime256v1 (256/128 Bits/secBits), signed using ecdsa-with-SHA256
*   Certificate level 1: Public key type EC/prime256v1 (256/128 Bits/secBits), signed using ecdsa-with-SHA384
*   Certificate level 2: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using ecdsa-with-SHA384
* Connected to google.com (142.250.197.14) port 443
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://google.com/
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: google.com]
* [HTTP/2] [1] [:path: /]
* [HTTP/2] [1] [user-agent: curl/8.14.1]
* [HTTP/2] [1] [accept: */*]
> GET / HTTP/2
> Host: google.com
> User-Agent: curl/8.14.1
> Accept: */*
>
* Request completely sent off
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
< HTTP/2 301
< location: https://www.google.com/
< content-type: text/html; charset=UTF-8
< content-security-policy-report-only: object-src 'none';base-uri 'self';script-src 'nonce-etdn54IcTHyGObO82DqZZQ' 'strict-dynamic' 'report-sample' 'unsafe-eval' 'unsafe-inline' https: http:;report-uri https://csp.withgoogle.com/csp/gws/other-hp
< date: Mon, 22 Dec 2025 12:27:16 GMT
< expires: Wed, 21 Jan 2026 12:27:16 GMT
< cache-control: public, max-age=2592000
< server: gws
< content-length: 220
< x-xss-protection: 0
< x-frame-options: SAMEORIGIN
< alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
<
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF="https://www.google.com/">here</A>.
</BODY></HTML>
* Connection #0 to host google.com left intact
  • * = curl 내부 로그(연결·TLS), > = 보낸 요청 헤더, < = 받은 응답 헤더, 본문은 prefix 없음.
  • TLS 1.3 핸드셰이크 흐름이 한 화면에 = ClientHello → ServerHello → cert 검증 → key exchange → Finished.
  • ALPN 으로 HTTP/2 협상, 인증서 체인 3 단(leaf → 중간 → root).

TCP 송·수신 — 커널 구현 관점


프로토콜 보편 흐름 (Segment·Packet·Frame encapsulation, MSS/MTU, OSI 매핑) 은 04. TCP & UDP > 송·수신 흐름과 encapsulation 참고. 여기는 리눅스 커널이 그 흐름을 어떻게 구현하나 — user/kernel 경계, buffer, syscall, driver, NIC — 에 집중.

user mode ↔ kernel mode 경계

송·수신 모두 데이터가 유저 공간 ↔ 커널 공간 사이를 한 번씩 복사된다. 그 복사가 write()·read() syscall.

+----------------------------------------+
| User mode (애플리케이션 영역)             |
|   Client Process                        |
|     ↓ data 작성                          |
|   File I/O Buffer (heap 의 buf 변수)     |
+----------------------------------------+
        ↕  write(fd, buf, len) / read(fd, buf, len)   (syscall 경계)
+----------------------------------------+
| Kernel mode                             |
|   Socket (fd → struct sock)             |
|     ↓                                    |
|   TCP send buffer  (= ss 의 Send-Q/Recv-Q) │
|     ↓ TCP 가 MSS 단위로 segment 만듦       │
|   IP layer (packet 만듦, 라우팅 결정)     │
|     ↓                                    |
|   Driver (ring buffer + DMA)             │
+----------------------------------------+
        ↕  H/W (DMA, 인터럽트)
+----------------------------------------+
| NIC (PHY + MAC)                         |
|   Frame 만들고 전기 신호로 송출            |
+----------------------------------------+

송신 단계

  1. Client Process = buf 변수에 데이터 작성 (user space heap).
  2. write(socket_fd, buf, len) 호출 → syscall 진입 → 커널이 bufTCP send buffer (커널 메모리) 로 복사.
  3. TCP = send buffer 데이터를 MSS 단위로 segment 화. seq·ack·window 부여.
  4. IP = segment 에 IP 헤더 부여, 라우팅 테이블 확인해 다음 hop 결정.
  5. Driver = packet 을 NIC 의 ring buffer 에 넣음 (DMA 로 NIC 메모리에 직접 쓰기).
  6. NIC = Ethernet 헤더 + FCS 부여, frame 으로 전기 신호 송출.

수신 단계

역순:

  1. NIC = frame 수신 → 인터럽트 (또는 NAPI polling) 로 커널 깨움.
  2. Driver = NIC 에서 frame 가져와 Ethernet 헤더 검증·제거.
  3. IP = packet 의 IP 헤더 검증, 자기 IP 면 위로 전달.
  4. TCP = segment 헤더 검증, 시퀀스 번호로 순서 정렬, TCP recv buffer 에 저장. ACK 송신.
  5. Application = read(socket_fd, buf, len) → 커널 recv buffer 에서 user buffer 로 복사.

그림 요소의 정확한 계층

요소계층메모
Client ProcessL7 ApplicationHTTP·SSH·custom app
Socket (API)L4~L7 경계 인터페이스 (학술적 L5)프로토콜 아님, “fd 를 통한 transport 접근”
TCPL4 Transportsegment 만듦
IPL3 Networkpacket 만듦, 라우팅
DriverL2 Data Link (SW)NIC 의 L2 기능을 OS 가 제어. L3 아님
NICL1 + L2 (PHY + MAC)MAC 부 = frame·체크섬, PHY 부 = 전기 신호

왜 user/kernel 경계가 중요한가

  • syscall = mode switch 비용 발생. user→kernel 전환 + 데이터 복사 (user buffer → kernel buffer).
  • 고성능 환경에선 이 복사가 병목 → zero-copy 기법 (sendfile(), splice(), MSG_ZEROCOPY 플래그).
  • 더 극단적 = 커널 바이패스 (DPDK, AF_XDP) — NIC 와 user space 가 직접 통신, 커널 네트워크 스택 자체를 안 거침. 위 ## 루프백과 127.0.0.1 의 “층위 비교” 와 같은 맥락.
  • TCP send/recv buffer 가 커널 안에 있으므로 Recv-Q·Send-Q 가 ss 로 보임 — 위 ## 네트워크 진단 도구### 패킷 버퍼 — 커널 vs 유저 공간 참고.

rmem_max·tcp_rmem — TCP buffer 튜닝

TCP buffer 의 정의 자체는 04. TCP & UDP > MSS vs TCP buffer vs window 참고. 여기는 그 buffer 의 커널 노브.

두 층의 sysctl

  • net.core.rmem_max / wmem_max = 모든 종류 소켓 buffer 의 절대 상한 (TCP·UDP·raw 다). application 이 setsockopt(SO_RCVBUF, ...) 으로 키울 수 있는 최대값.
  • net.ipv4.tcp_rmem / tcp_wmem = [min, default, max] 3 값. TCP 의 자동 조정(auto-tuning) 범위. 그 max 가 위 rmem_max 를 넘을 수 없음.

디폴트

  • rmem_max212992 (208 KB)
  • tcp_rmem4096 87380 6291456 (4KB / 85KB / 6MB)
  • 일반 환경엔 충분, BDP 큰 환경엔 부족.

BDP (Bandwidth-Delay Product)

  • 회선 대역 × RTT = “in-flight 가능한 데이터양”
  • 10 Gbps × 10ms ≈ 12 MB. 1 Gbps × 100ms ≈ 12 MB
  • buffer 가 BDP 보다 작으면 TCP window 가 거기 막혀 throughput 저하

AWS 컨텍스트

  • cross-AZ (수 ms RTT) / cross-region (수십 ms RTT) / S3 download / EBS·EFS 대용량 IO 가 BDP 큰 케이스
  • rmem_max 를 25 MB (26214400) 같이 올려 충분한 BDP 흡수

단점

  • 메모리 사용 증가 = 연결 수 × 평균 buffer 크기. 수만 연결 받는 서버에선 RAM 부담.

운영 사례

확인

sysctl net.core.rmem_max net.core.wmem_max
sysctl net.ipv4.tcp_rmem net.ipv4.tcp_wmem
ss -ti                  # 개별 connection 의 mss·snd_wnd 등

References