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

v6.6 / 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)
		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;
}

```

### 1498행

```c

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

```

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

### 1499행

```c

			 struct socket **res, int kern)

```

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

### 1501행

```c

	int err;

```

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

### 1502행

```c

	struct socket *sock;

```

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

### 1503행

```c

	const struct net_proto_family *pf;

```

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

### 1508행

```c

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

```

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

### 1509행

```c

		return -EAFNOSUPPORT;

```

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

### 1510행

```c

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

```

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

### 1511행

```c

		return -EINVAL;

```

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

### 1518행

```c

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

```

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

### 1519행

```c

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

```

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

### 1520행

```c

			     current->comm);

```

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

### 1521행

```c

		family = PF_PACKET;

```

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

### 1524행

```c

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

```

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

### 1525행

```c

	if (err)

```

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

### 1526행

```c

		return err;

```

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

### 1533행

```c

	sock = sock_alloc();

```

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

### 1534행

```c

	if (!sock) {

```

할당 실패를 검사합니다.

### 1535행

```c

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

```

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

### 1536행

```c

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

```

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

### 1540행

```c

	sock->type = type;

```

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

### 1542행

```c

#ifdef CONFIG_MODULES

```

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

### 1549행

```c

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

```

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

### 1550행

```c

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

```

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

### 1551행

```c

#endif

```

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

### 1553행

```c

	rcu_read_lock();

```

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

### 1554행

```c

	pf = rcu_dereference(net_families[family]);

```

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

### 1555행

```c

	err = -EAFNOSUPPORT;

```

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

### 1556행

```c

	if (!pf)

```

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

### 1557행

```c

		goto out_release;

```

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

### 1563행

```c

	if (!try_module_get(pf->owner))

```

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

### 1564행

```c

		goto out_release;

```

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

### 1567행

```c

	rcu_read_unlock();

```

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

### 1569행

```c

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

```

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

### 1570행

```c

	if (err < 0)

```

프로토콜 create 함수가 실패했는지 검사합니다. v6.6의 이 본문은 별도의 sock->sk 디버그 경고 없이 바로 오류 정리로 갑니다.

### 1571행

```c

		goto out_module_put;

```

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

### 1577행

```c

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

```

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

### 1578행

```c

		goto out_module_busy;

```

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

### 1584행

```c

	module_put(pf->owner);

```

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

### 1585행

```c

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

```

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

### 1586행

```c

	if (err)

```

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

### 1587행

```c

		goto out_sock_release;

```

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

### 1588행

```c

	*res = sock;

```

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

### 1590행

```c

	return 0;

```

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

### 1592행

```c

out_module_busy:

```

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

### 1593행

```c

	err = -EAFNOSUPPORT;

```

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

### 1594행

```c

out_module_put:

```

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

### 1595행

```c

	sock->ops = NULL;

```

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

### 1596행

```c

	module_put(pf->owner);

```

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

### 1597행

```c

out_sock_release:

```

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

### 1598행

```c

	sock_release(sock);

```

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

### 1599행

```c

	return err;

```

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

### 1601행

```c

out_release:

```

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

### 1602행

```c

	rcu_read_unlock();

```

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

### 1603행

```c

	goto out_sock_release;

```

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

