Linux v6.18.37 · 개념과 코드 읽기

네트워크 네임스페이스: 새 네트워크 환경의 상태를 나누는 방법

이 코드는 어떤 문제를 푸나요?

네트워크 네임스페이스는 인터페이스·라우팅·소켓 관련 네트워크 상태를 서로 분리해 관리하게 합니다. 새 struct net만 할당한다고 모든 하위 기능이 준비되지는 않습니다. 등록된 pernet_operations를 따라 각 기능의 전용 상태를 할당하고 초기화해야 하며, ops_init은 그중 한 기능을 한 네임스페이스에 설치하는 전체 절차입니다.

읽을 범위: v6.18.37 · net/core/net_namespace.c · ops_init 120–152행입니다. 아래에 이 범위의 원문과 각 줄의 설명을 실었습니다. 주제 전체의 흐름과 다른 경로는 기존 분석에서 함께 읽으실 수 있습니다.

먼저 알아둘 개념

struct net

하나의 네트워크 네임스페이스를 나타냅니다. 물리 NIC 하나와 일대일인 객체가 아니며 loopback 등 네임스페이스별 상태를 포함합니다.

pernet_operations

각 네트워크 기능이 네임스페이스 생성·정리 때 수행할 동작을 등록하는 표입니다. init 콜백은 네임스페이스별 초기화를 담당합니다.

generic 슬롯

기능별 id로 해당 네임스페이스의 전용 데이터를 찾는 저장소입니다. 모든 기능의 구체적 구조체를 struct net에 직접 넣지 않아도 됩니다.

처음 읽을 때

같은 네트워크 기능이 두 네임스페이스에서 별도의 상태를 가져야 하는 이유를 생각하십시오. 코드가 공유된다는 사실과 런타임 상태까지 공유한다는 주장은 다릅니다.

더 깊이 살펴볼 때

메모리 할당, 슬롯 설치, 기능 초기화가 각각 실패하는 경우의 정리 순서를 따라가십시오. RCU 포인터 접근이 외부 pernet_ops_rwsem 보호를 전제로 한다는 점도 중요합니다.

그림으로 보는 변화

네트워크 네임스페이스: 새 네트워크 환경의 상태를 나누는 방법의 단계별 개념 그림
각 단계에 화살표 의미와 생략 범위를 표시했습니다. 주소·숫자 예제는 실제 장치 값을 뜻하지 않습니다.
1단계 설명

1단계 고정

GIF 원본 열기

1. 기능별 공간을 만듭니다

ops의 크기로 0 초기화 메모리를 준비합니다. 전용 id가 없는 기능은 이 단계를 생략할 수 있습니다.

화살표는 기능 정의에서 네임스페이스 전용 인스턴스를 만드는 동작입니다.

2. 네임스페이스에 연결합니다

id 슬롯에 데이터를 설치하고 기능의 init 콜백을 실행합니다.

화살표는 net의 슬롯에서 해당 데이터로 가는 참조입니다.

3. 성공하거나 정리합니다

초기화가 실패하면 설치한 슬롯을 비우고 할당한 메모리를 해제합니다.

되돌아가는 화살표는 실패 시 자원 정리이며 다른 네임스페이스를 제거하지 않습니다.

ops_init를 한 줄씩 읽기

줄 번호는 v6.18.37 원문 기준입니다. 주석·빈 줄을 포함한 함수 전체를 먼저 보고, 그 아래에서 각 줄을 설명합니다.

static int ops_init(const struct pernet_operations *ops, struct net *net)
{
	struct net_generic *ng;
	int err = -ENOMEM;
	void *data = NULL;

	if (ops->id) {
		data = kzalloc(ops->size, GFP_KERNEL);
		if (!data)
			goto out;

		err = net_assign_generic(net, *ops->id, data);
		if (err)
			goto cleanup;
	}
	err = 0;
	if (ops->init)
		err = ops->init(net);
	if (!err)
		return 0;

	if (ops->id) {
		ng = rcu_dereference_protected(net->gen,
					       lockdep_is_held(&pernet_ops_rwsem));
		ng->ptr[*ops->id] = NULL;
	}

cleanup:
	kfree(data);

out:
	return err;
}
static int ops_init(const struct pernet_operations *ops, struct net *net)

한 네트워크 기능의 초기화 규칙 ops를 특정 네임스페이스 net에 적용합니다.

	struct net_generic *ng;

net의 generic 포인터 표를 다룰 지역변수입니다.

	int err = -ENOMEM;

메모리 할당이 바로 실패하면 반환할 -ENOMEM을 기본값으로 둡니다.

	void *data = NULL;

아직 할당한 데이터가 없음을 NULL로 나타냅니다.

	if (ops->id) {

이 기능이 네임스페이스별 generic 데이터 슬롯을 요구하는지 확인합니다.

		data = kzalloc(ops->size, GFP_KERNEL);

ops->size만큼 0으로 채운 메모리를 할당합니다. GFP_KERNEL 경로는 필요한 경우 잠들 수 있습니다.

		if (!data)

메모리 할당 실패를 검사합니다.

			goto out;

할당한 것이 없으므로 별도 해제 없이 오류 반환 위치로 갑니다.

		err = net_assign_generic(net, *ops->id, data);

기능 id에 해당하는 net의 슬롯에 새 데이터를 설치합니다.

		if (err)

슬롯 확장이나 설치가 실패했는지 확인합니다.

			goto cleanup;

설치하지 못한 메모리를 해제하는 위치로 갑니다.

	err = 0;

기능 초기화 함수를 호출하지 않는 경우에도 성공할 수 있도록 결과를 0으로 바꿉니다.

	if (ops->init)

이 기능이 init 콜백을 제공하는지 확인합니다.

		err = ops->init(net);

해당 네임스페이스를 넘겨 기능별 초기화를 수행합니다.

	if (!err)

초기화가 성공했거나 콜백이 없어서 정상인지를 확인합니다.

		return 0;

성공한 경우 데이터는 네임스페이스가 계속 사용하므로 해제하지 않습니다.

	if (ops->id) {

실패한 초기화 전에 generic 슬롯을 설치한 경로인지 확인합니다.

		ng = rcu_dereference_protected(net->gen,

보호된 조건에서 net의 generic 포인터 표를 얻습니다.

					       lockdep_is_held(&pernet_ops_rwsem));

pernet_ops_rwsem을 보유한 상태라는 잠금 검증 조건을 RCU 접근에 전달합니다.

		ng->ptr[*ops->id] = NULL;

곧 해제할 데이터를 가리키던 슬롯을 NULL로 비웁니다.

cleanup:

슬롯 설치 실패와 기능 초기화 실패가 공유하는 메모리 정리 위치입니다.

	kfree(data);

앞서 할당한 전용 데이터를 해제합니다. NULL이면 아무것도 해제하지 않습니다.

out:

오류를 호출자에게 돌려줄 공통 위치입니다.

	return err;

메모리 또는 기능 초기화 단계에서 얻은 오류를 반환합니다.

함께 생각해 볼 질문

모든 네트워크 기능이 id를 가져야 하나요?

이 함수는 ops->id가 없는 경우를 허용합니다. 그런 기능은 여기서 generic 전용 메모리를 할당하지 않고 init만 사용할 수 있습니다.

kzalloc의 0 초기화만 하면 준비가 끝나나요?

아닙니다. 기능별 목록·타이머·정책 등을 준비하는 init 콜백이 별도로 필요할 수 있습니다.

초기화 실패 후 슬롯부터 비우는 이유는 무엇인가요?

해제한 메모리가 여전히 네임스페이스의 유효한 전용 상태처럼 보이지 않게 하기 위해서입니다.

출처와 읽은 범위

Linux stable v6.18.37 · net/core/net_namespace.c

해당 버전 원본 파일 · 기존 코드 분석 · 설명 원고

맨 위로 ↑