# 소켓 생성: 정수 핸들보다 먼저 필요한 프로토콜 객체

v6.18.37 / net/socket.c

socket 시스템 호출은 단순히 숫자 하나를 발급하는 작업이 아닙니다. 주소 체계와 소켓 종류를 검사하고, 사용할 프로토콜 구현을 찾은 뒤 그 구현의 상태를 만들어야 합니다. __sock_create 전체는 공통 socket 객체와 프로토콜 객체를 연결하며 모듈과 보안 검사의 실패도 정리합니다. 파일 디스크립터 배정은 이 함수 바깥의 후속 단계입니다.

## __sock_create

```c

int __sock_create(struct net *net, int family, int type, int protocol,
			 struct socket **res, int kern)
{
	int err;
	struct socket *sock;
	const struct net_proto_family *pf;

	/*
	 *      Check protocol is in range
	 */
	if (family < 0 || family >= NPROTO)
		return -EAFNOSUPPORT;
	if (type < 0 || type >= SOCK_MAX)
		return -EINVAL;

	/* Compatibility.

	   This uglymoron is moved from INET layer to here to avoid
	   deadlock in module load.
	 */
	if (family == PF_INET && type == SOCK_PACKET) {
		pr_info_once("%s uses obsolete (PF_INET,SOCK_PACKET)\n",
			     current->comm);
		family = PF_PACKET;
	}

	err = security_socket_create(family, type, protocol, kern);
	if (err)
		return err;

	/*
	 *	Allocate the socket and allow the family to set things up. if
	 *	the protocol is 0, the family is instructed to select an appropriate
	 *	default.
	 */
	sock = sock_alloc();
	if (!sock) {
		net_warn_ratelimited("socket: no more sockets\n");
		return -ENFILE;	/* Not exactly a match, but its the
				   closest posix thing */
	}

	sock->type = type;

#ifdef CONFIG_MODULES
	/* Attempt to load a protocol module if the find failed.
	 *
	 * 12/09/1996 Marcin: But! this makes REALLY only sense, if the user
	 * requested real, full-featured networking support upon configuration.
	 * Otherwise module support will break!
	 */
	if (rcu_access_pointer(net_families[family]) == NULL)
		request_module("net-pf-%d", family);
#endif

	rcu_read_lock();
	pf = rcu_dereference(net_families[family]);
	err = -EAFNOSUPPORT;
	if (!pf)
		goto out_release;

	/*
	 * We will call the ->create function, that possibly is in a loadable
	 * module, so we have to bump that loadable module refcnt first.
	 */
	if (!try_module_get(pf->owner))
		goto out_release;

	/* Now protected by module ref count */
	rcu_read_unlock();

	err = pf->create(net, sock, protocol, kern);
	if (err < 0) {
		/* ->create should release the allocated sock->sk object on error
		 * and make sure sock->sk is set to NULL to avoid use-after-free
		 */
		DEBUG_NET_WARN_ONCE(sock->sk,
				    "%ps must clear sock->sk on failure, family: %d, type: %d, protocol: %d\n",
				    pf->create, family, type, protocol);
		goto out_module_put;
	}

	/*
	 * Now to bump the refcnt of the [loadable] module that owns this
	 * socket at sock_release time we decrement its refcnt.
	 */
	if (!try_module_get(sock->ops->owner))
		goto out_module_busy;

	/*
	 * Now that we're done with the ->create function, the [loadable]
	 * module can have its refcnt decremented
	 */
	module_put(pf->owner);
	err = security_socket_post_create(sock, family, type, protocol, kern);
	if (err)
		goto out_sock_release;
	*res = sock;

	return 0;

out_module_busy:
	err = -EAFNOSUPPORT;
out_module_put:
	sock->ops = NULL;
	module_put(pf->owner);
out_sock_release:
	sock_release(sock);
	return err;

out_release:
	rcu_read_unlock();
	goto out_sock_release;
}

```

### 1535행

```c

int __sock_create(struct net *net, int family, int type, int protocol,

```

네임스페이스와 주소 체계, 소켓 종류, 프로토콜 번호를 받습니다.

### 1536행

```c

			 struct socket **res, int kern)

```

생성한 socket을 기록할 결과 포인터와 커널 내부용 생성인지 나타내는 kern 값을 받습니다.

### 1538행

```c

	int err;

```

단계별 오류를 보관할 변수입니다.

### 1539행

```c

	struct socket *sock;

```

새로 할당할 공통 struct socket의 포인터입니다. struct sock 포인터와 구분합니다.

### 1540행

```c

	const struct net_proto_family *pf;

```

주소 체계에 등록된 create 함수와 모듈 정보를 가리킵니다.

### 1545행

```c

	if (family < 0 || family >= NPROTO)

```

family 값이 프로토콜 표의 유효 범위 안에 있는지 확인합니다.

### 1546행

```c

		return -EAFNOSUPPORT;

```

지원할 수 없는 주소 체계 범위를 오류로 돌려줍니다.

### 1547행

```c

	if (type < 0 || type >= SOCK_MAX)

```

소켓 종류도 유효한 열거값 범위인지 검사합니다.

### 1548행

```c

		return -EINVAL;

```

잘못된 종류를 -EINVAL로 반환합니다.

### 1555행

```c

	if (family == PF_INET && type == SOCK_PACKET) {

```

오래된 PF_INET·SOCK_PACKET 조합의 호환 경로인지 확인합니다.

### 1556행

```c

		pr_info_once("%s uses obsolete (PF_INET,SOCK_PACKET)\n",

```

구식 인터페이스를 사용한다는 메시지를 한 번 출력합니다.

### 1557행

```c

			     current->comm);

```

메시지에 현재 태스크 이름을 넣습니다.

### 1558행

```c

		family = PF_PACKET;

```

구식 요청을 패킷 소켓 주소 체계로 변환합니다.

### 1561행

```c

	err = security_socket_create(family, type, protocol, kern);

```

보안 모듈에 이 종류의 소켓 생성을 허용하는지 묻습니다.

### 1562행

```c

	if (err)

```

보안 검사 실패 여부를 확인합니다.

### 1563행

```c

		return err;

```

아직 소켓을 할당하지 않았으므로 해당 오류만 반환합니다.

### 1570행

```c

	sock = sock_alloc();

```

공통 socket 객체를 할당합니다.

### 1571행

```c

	if (!sock) {

```

할당 실패를 검사합니다.

### 1572행

```c

		net_warn_ratelimited("socket: no more sockets\n");

```

반복 오류가 로그를 과도하게 채우지 않도록 빈도를 제한해 메시지를 출력합니다.

### 1573행

```c

		return -ENFILE;	/* Not exactly a match, but its the

```

소켓 객체를 만들지 못한 오류를 -ENFILE로 반환합니다. 실제 디스크 파일 수를 센 결과라는 뜻은 아닙니다.

### 1577행

```c

	sock->type = type;

```

생성한 socket에 요청한 종류를 저장합니다.

### 1579행

```c

#ifdef CONFIG_MODULES

```

프로토콜 구현을 별도 커널 모듈로 가져올 수 있는 빌드인지 확인합니다. 켜져 있으면 아직 등록되지 않은 소켓 주소 계열을 request_module로 불러오려 시도하며, 꺼진 빌드는 그 자동 로드 경로를 포함하지 않습니다. 이 선택은 전처리 단계에서 이루어지며, CPU가 실행 중 이 줄의 조건을 검사하지 않습니다.

### 1586행

```c

	if (rcu_access_pointer(net_families[family]) == NULL)

```

요청한 family 구현이 현재 등록되어 있지 않은지 가볍게 확인합니다.

### 1587행

```c

		request_module("net-pf-%d", family);

```

해당 family의 프로토콜 모듈을 로드하도록 요청합니다. 로드 성공을 가정하지 않고 아래에서 다시 조회합니다.

### 1588행

```c

#endif

```

모듈 자동 로드 조건부 코드가 끝납니다.

### 1590행

```c

	rcu_read_lock();

```

등록된 프로토콜 포인터를 안전하게 읽도록 RCU 읽기 구간을 시작합니다.

### 1591행

```c

	pf = rcu_dereference(net_families[family]);

```

family 인덱스로 실제 프로토콜 구현을 얻습니다.

### 1592행

```c

	err = -EAFNOSUPPORT;

```

구현을 못 찾는 경로의 기본 오류를 설정합니다.

### 1593행

```c

	if (!pf)

```

등록된 구현이 없는지 검사합니다.

### 1594행

```c

		goto out_release;

```

RCU 보호를 끝내고 socket을 해제하는 경로로 갑니다.

### 1600행

```c

	if (!try_module_get(pf->owner))

```

create 함수가 실행되는 동안 구현 모듈이 제거되지 않도록 참조를 얻습니다.

### 1601행

```c

		goto out_release;

```

제거 중인 모듈 등으로 참조를 얻지 못하면 생성하지 않고 정리합니다.

### 1604행

```c

	rcu_read_unlock();

```

이제 모듈 참조로 구현 수명을 보호하므로 RCU 읽기 구간을 끝냅니다.

### 1606행

```c

	err = pf->create(net, sock, protocol, kern);

```

주소 체계별 create 함수에 네임스페이스와 socket을 넘겨 프로토콜 상태를 만듭니다.

### 1607행

```c

	if (err < 0) {

```

프로토콜 생성 실패를 검사합니다.

### 1611행

```c

		DEBUG_NET_WARN_ONCE(sock->sk,

```

실패한 create 함수가 sock->sk를 정리했는지 디버그 경고로 확인합니다.

### 1612행

```c

				    "%ps must clear sock->sk on failure, family: %d, type: %d, protocol: %d\n",

```

실패 시 포인터를 NULL로 해야 한다는 진단 문구입니다. 해제한 프로토콜 객체를 다시 사용하지 않도록 하는 규칙입니다.

### 1613행

```c

				    pf->create, family, type, protocol);

```

문제를 일으킨 create 함수와 생성 인자들을 경고에 표시합니다.

### 1614행

```c

		goto out_module_put;

```

생성용 모듈 참조와 공통 socket을 내려놓는 경로로 갑니다.

### 1621행

```c

	if (!try_module_get(sock->ops->owner))

```

소켓 연산을 제공할 모듈을 소켓 수명 동안 유지하기 위한 참조를 얻습니다.

### 1622행

```c

		goto out_module_busy;

```

연산 모듈을 유지할 수 없으면 생성 성공으로 넘기지 않고 정리합니다.

### 1628행

```c

	module_put(pf->owner);

```

create 함수 실행이 끝났으므로 생성 단계에만 필요했던 모듈 참조를 내려놓습니다.

### 1629행

```c

	err = security_socket_post_create(sock, family, type, protocol, kern);

```

만들어진 socket에 대해 보안 모듈의 후속 검사를 수행합니다.

### 1630행

```c

	if (err)

```

생성 후 보안 검사에서 거부됐는지 확인합니다.

### 1631행

```c

		goto out_sock_release;

```

이미 만들어진 소켓을 정상 해제 경로로 정리합니다.

### 1632행

```c

	*res = sock;

```

모든 단계를 통과한 socket 포인터를 호출자의 결과 위치에 넣습니다.

### 1634행

```c

	return 0;

```

생성 성공을 반환합니다. 아직 사용자 fd 번호를 반환하는 위치는 아닙니다.

### 1636행

```c

out_module_busy:

```

소켓 연산 모듈을 유지할 수 없을 때 도달하는 오류 위치입니다.

### 1637행

```c

	err = -EAFNOSUPPORT;

```

주소 체계 사용을 계속할 수 없다는 오류를 설정합니다.

### 1638행

```c

out_module_put:

```

생성용 모듈 참조를 정리할 공통 위치입니다.

### 1639행

```c

	sock->ops = NULL;

```

확보하지 못한 연산 모듈에 대해 잘못된 해제 콜백을 실행하지 않도록 연산 표를 비웁니다.

### 1640행

```c

	module_put(pf->owner);

```

생성 콜백의 모듈 참조를 내려놓습니다.

### 1641행

```c

out_sock_release:

```

공통 socket 자원을 해제하는 위치입니다.

### 1642행

```c

	sock_release(sock);

```

현재 설정된 연산과 객체 상태에 맞춰 소켓을 정리합니다.

### 1643행

```c

	return err;

```

실패 원인을 호출자에게 반환합니다.

### 1645행

```c

out_release:

```

프로토콜 표를 조회하는 RCU 구간 안에서 실패했을 때 오는 위치입니다.

### 1646행

```c

	rcu_read_unlock();

```

아직 유지한 RCU 읽기 보호를 끝냅니다.

### 1647행

```c

	goto out_sock_release;

```

할당한 공통 socket을 해제하는 공통 경로로 이동합니다.

