Microsoft MVP성태의 닷넷 이야기
Linux: 73. Linux 측의 socat을 이용한 Hyper-V 호스트와의 vsock 테스트 [링크 복사], [링크+제목 복사],
조회: 7891
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일

(시리즈 글이 6개 있습니다.)
닷넷: 2270. C# - Hyper-V Socket 통신(AF_HYPERV, AF_VSOCK)을 위한 EndPoint 사용자 정의
; https://www.sysnet.pe.kr/2/0/13657

닷넷: 2272. C# - Hyper-V Socket 통신(AF_HYPERV, AF_VSOCK)의 VMID Wildcards 유형
; https://www.sysnet.pe.kr/2/0/13663

Linux: 73. Linux 측의 socat을 이용한 Hyper-V 호스트와의 vsock 테스트
; https://www.sysnet.pe.kr/2/0/13665

Linux: 74. C++ - Vsock 예제 (Hyper-V Socket 연동)
; https://www.sysnet.pe.kr/2/0/13666

닷넷: 2273. C# - 리눅스 환경에서의 Hyper-V Socket 연동 (AF_VSOCK)
; https://www.sysnet.pe.kr/2/0/13667

개발 환경 구성: 721. WSL 2에서의 Hyper-V Socket 연동
; https://www.sysnet.pe.kr/2/0/13713




Linux 측의 socat을 이용한 Hyper-V 호스트와의 vsock 테스트

vsock을 지원하는 socat(SO cket CAT)은 1.7.4 버전부터인데요, (따라서 이것을 테스트하려면 Ubuntu의 경우 22.04 이상이 필요합니다.)

$ sudo apt install socat

자, 그럼 이걸로 Hyper-V 호스트와 리눅스 VM 간의 통신을 테스트해 볼 텐데요, 우선 호스트 측의 윈도우 프로그램은 지난번에 만들어 둔 C# 응용 프로그램으로 대체하겠습니다.

하지만, 그 예제를 그대로 사용할 수는 없고 한 가지 변경을 해야 하는데요, 바로 Service ID를 리눅스 vsock과 호환하도록 맞춰주어야 합니다. 왜냐하면, vsock의 sockaddr 구조체는,

// 버전에 따라 필드의 차이는 있지만 전체적으로 16바이트의 크기는 동일합니다.

struct sockaddr_vm {
	__kernel_sa_family_t svm_family;
	unsigned short svm_reserved1;
	unsigned int svm_port;
	unsigned int svm_cid;
	__u8 svm_flags;
	unsigned char svm_zero[sizeof(struct sockaddr) -
			       sizeof(sa_family_t) -
			       sizeof(unsigned short) -
			       sizeof(unsigned int) -
			       sizeof(unsigned int) -
			       sizeof(__u8)];
};

Guid를 사용하는 윈도우(Hyper-V Socket)와 달리 "unsigned int" 타입의 svm_cid, svm_port를 쓰기 때문입니다. 이러한 차이점을 메우기 위해, 윈도우 측에서는 vsock과 호환하기 위해 특별한 GUID를 소개하고 있습니다.

// Hyper-V Socket Linux guest VSOCK template GUID

struct __declspec(uuid("00000000-facb-11e6-bd58-64006a7986d3")) VSockTemplate{};

 /*
  * GUID example = __uuidof(VSockTemplate);
  * example.Data1 = 2761; // 0x00000AC9
  */

즉, "00000000-facb-11e6-bd58-64006a7986d3" 값을 써야 하고, 앞 부분의 "00000000" 측에 svm_port에 해당하는 값을 16진수로 쓰는 식입니다. 가령, 19000 포트를 쓰고 싶다면 그것의 16진수 값인 0x4a38을 적용해 "00004A38-facb-11e6-bd58-64006a7986d3" 값으로 해야 하는 것입니다.

마지막으로, 저 값을 "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\GuestCommunicationServices" 하위에 등록하는 것을 잊지 마시고.




실습을 위해, 이제 지난번 소스코드에서 GcsGuid 값만 바꿔주고,

// 19000번 포트를 사용한다고 가정

public static readonly Guid GcsGuid = new Guid("00004A38-facb-11e6-bd58-64006a7986d3");

(레지스트리 등록 잊지 마시고) 빌드 후 서버 모드로 실행해 둔 후,

ConsoleApp1.exe /server any

Hyper-V에 설치한 Ubuntu 22.04 VM에서 socat을 이용해 다음과 같이 테스트할 수 있습니다.

// socat 실행 후, "test" 입력하고 enter 키를 누르면 ConsoleApp1.exe 측에서 반환하는 "Hello: test"가 출력됩니다.

$ socat - VSOCK-CONNECT:2:19000
test
Hello: test
$

VSOCK-CONNECT 인자와 함께 "2:19000" 값을 주었는데요, 여기서 2는 VMADDR_CID_HOST로 정의된 상수입니다.

/* Use this as the destination CID in an address when referring to the host
 * (any process other than the hypervisor).  VMCI relies on it being 2, but
 * this would be useful for other transports too.
 */

#define VMADDR_CID_HOST 2

즉, VM에서 호스트 측으로 연결 시 svm_cid 필드에 설정해야 하는 값으로, Hyper-V Socket의 "HV_GUID_PARENT(a42e7cda-d03f-480c-9cc2-a4de20abb878)"에 해당한다고 보면 됩니다.




리눅스 측의 vsock 연결을 클라이언트로 했으니 이제 반대로 서버로써도 테스트해야겠죠? ^^

이를 위해 socat을 서버 모드로 실행해 두는데요,

$ socat - VSOCK-LISTEN:19000

역으로 Hyper-V 호스트 측에서는 (전과 다름없이) VM ID를 통해 접속하면 됩니다.

// socat을 실행한 Linux VM의 VM ID가 "f952c5ef-ea6c-4c83-b52c-38f31cf68b61"인 경우,

ConsoleApp1.exe /client f952c5ef-ea6c-4c83-b52c-38f31cf68b61

ConsoleApp1.exe 코드에서는 어차피 Service ID를 "00004A38-facb-11e6-bd58-64006a7986d3"로 사용해 19000 포트로 연결할 것이므로, 결국 필요한 것은 VM ID뿐입니다.

참고로, 윈도우의 경우 Server 측 바인딩이 HV_GUID_WILDCARD와 HV_GUID_LOOPBACK이 가능했는데요, 리눅스도 그와 유사하게 VMADDR_CID_ANY와 (한때 VMADDR_CID_RESERVED였던) VMADDR_CID_LOCAL이 있습니다.

/* The vSocket equivalent of INADDR_ANY.  This works for the svm_cid field of
 * sockaddr_vm and indicates the context ID of the current endpoint.
 */
#define VMADDR_CID_ANY -1U

/* Use this as the destination CID in an address when referring to the
 * local communication (loopback).
 * (This was VMADDR_CID_RESERVED, but even VMCI doesn't use it anymore,
 * it was a legacy value from an older release).
 */
#define VMADDR_CID_LOCAL 1

socat이 어떤 값과 바인딩하는지는 모르겠지만, 위의 설명만 봐서는 VMADDR_CID_ANY인 듯합니다. 이런 경우, 역시나 윈도우에서처럼 같은 VM 내에서는 VMADDR_CID_LOCAL로 연결할 수 있는데요, 따라서 같은 VM 내에서 2개의 터미널을 열어두고 각각 다음과 같이 테스트하면 서로 연결이 됩니다.

// 리눅스 VM의 터미널 A

$ socat - VSOCK-LISTEN:19000

// 리눅스 VM의 터미널 B

$ socat - VSOCK-CONNECT:1:19000

"1:19000"에서 "1"은 VMADDR_CID_LOCAL 값에 해당합니다. 그런 의미에서 본다면 VMADDR_CID_ANY와 VMADDR_CID_LOCAL은 Hyper-V Socket으로 매핑한다면 각각 HV_GUID_WILDCARD와 HV_GUID_LOOPBACK이 됩니다.




참고로, Linux 측에서 vsock 지원 여부를 모듈 열거를 통해 확인할 수 있다고 합니다.

$ lsmod | grep vsock
vsock_loopback                      12288   0
vmw_vsock_virtio_transport_common   61440   1 vsock_loopback
vmw_vsock_vmci_transport            45056   0
vsock                               61440   4 vmw_vsock_virtio_transport_common,vsock_loopback,hv_sock,vmw_vsock_vmci_transport
vmw_vmci                            106496  1 vmw_vsock_vmci_transport

또한, 윈도우의 Hyper-V에서 호스팅하는 것이 아닌, Linux를 호스트로 Linux VM을 돌리는 경우에는 svm_cid 값을 VM 시작 시의 인자로 전달해야 합니다. 가령 qemu의 경우,

// https://github.com/stefano-garzarella/socat-vsock/blob/be46eaf169eea1c63b3d90772610a8733a346e30/EXAMPLES#L236

//////////////////////////////////////////////////////////////////////////////
// VSOCK
# start a linux VM with cid=21
#    qemu-system-x86_64 -m 1G -smp 2 -cpu host -M accel=kvm \
#     -drive if=virtio,file=/path/to/fedora.img,format=qcow2  \
#     -device vhost-vsock-pci,guest-cid=21

# guest listens on port 1234 and host connects to it
guest$ socat - VSOCK-LISTEN:1234
host$ socat - VSOCK-CONNECT:21:1234

# host (well know CID_HOST = 2) listens on port 4321 and guest connects to it
host$ socat - VSOCK-LISTEN:4321
guest$ socat - VSOCK-CONNECT:2:4321

# ssh over vsock (guest-cid = 21)
guest$ socat VSOCK-LISTEN:22,reuseaddr,fork TCP:localhost:22
host$ socat TCP4-LISTEN:22222,reuseaddr,fork VSOCK-CONNECT:21:22
host$ ssh -p 22222 user@localhost

guest-cid 값을 통해 CID를 지정할 수 있습니다.

마지막으로, vsock이 지원되면 /dev/vsock 파일이 열거되는데요,

$ ls /dev/vsock -l
crw-rw-rw- 1 root root 10, 118  7월  2 09:33 /dev/vsock

이 파일에 대해 IOCTL_VM_SOCKETS_GET_LOCAL_CID 명령을 보내면 (qemu의 경우 guest-cid 값으로 전달한) CID 값을 얻는 것이 가능합니다.

#!/usr/bin/env python3
import socket
import struct
import fcntl

with open("/dev/vsock", "rb") as fd:
    r = fcntl.ioctl(fd, socket.IOCTL_VM_SOCKETS_GET_LOCAL_CID, "    ")
    cid = struct.unpack("I", r)[0]

    print("Local CID: {}".format(cid))

# https://gist.github.com/stefano-garzarella/b4971c2bed3c632b48c5e2a823c1cbe1

(qemu 등으로 guest-cid를 설정하지 않은) VM 또는 호스트의 경우에는 CID 값이 4294967295로 나옵니다.




Ubuntu 20.04에서 기본 설치하는 socat은,

$ apt show socat
Package: socat
Version: 1.7.3.3-2
...[생략]...

$ sudo apt install socat

1.7.3 버전인데요, vsock을 지원하지 않습니다.

SOCAT now supports AF_VSOCK
; https://stefano-garzarella.github.io/posts/2021-01-22-socat-vsock/

22.04 우분투 repo의 deb 파일을 직접 다운로드해,

socat_1.7.4.1-3ubuntu4_amd64.deb
; https://ubuntu.pkgs.org/22.04/ubuntu-main-amd64/socat_1.7.4.1-3ubuntu4_amd64.deb.html
; http://archive.ubuntu.com/ubuntu/pool/main/s/socat/socat_1.7.4.1-3ubuntu4_amd64.deb

설치하면,

$ wget http://archive.ubuntu.com/ubuntu/pool/main/s/socat/socat_1.7.4.1-3ubuntu4_amd64.deb
$ sudo apt install ./socat_1.7.4.1-3ubuntu4_amd64.deb

이런 오류가 발생할 것입니다.

$ sudo apt install ./socat_1.7.4.1-3ubuntu4_amd64.deb
...[생략]...

The following packages have unmet dependencies:
 socat : Depends: libc6 (>= 2.34) but 2.31-0ubuntu9.16 is to be installed
         Depends: libssl3 (>= 3.0.0~~alpha1) but it is not installable
E: Unable to correct problems, you have held broken packages.

아마도 저게 동작하려면, socat 빌드를 glibc 2.34 버전을 대상으로, libssl3도 함께 빌드해 링크해야 할 것입니다. 저야 리눅스 초보자라... ^^;




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 7/6/2024]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 31  32  33  34  35  36  37  38  39  40  41  42  43  [44]  45  ...
NoWriterDateCnt.TitleFile(s)
12835정성태9/7/202117276.NET Framework: 1116. C# 10 - (15) CallerArgumentExpression 특성 추가 [2]파일 다운로드1
12834정성태9/7/202115420오류 유형: 762. Visual Studio 2019 Build Tools - 'C:\Program' is not recognized as an internal or external command, operable program or batch file.
12833정성태9/6/202113527VC++: 150. Golang - TCP client/server echo 예제 코드파일 다운로드1
12832정성태9/6/202115544VC++: 149. Golang - 인터페이스 포인터가 의미 있을까요?
12831정성태9/6/202112717VC++: 148. Golang - 채널에 따른 다중 작업 처리파일 다운로드1
12830정성태9/6/202117042오류 유형: 761. Internet Explorer에서 파일 다운로드 시 "Your current security settings do not allow this file to be downloaded." 오류
12829정성태9/5/202118373.NET Framework: 1115. C# 10 - (14) 구조체 타입에 기본 생성자 정의 가능파일 다운로드1
12828정성태9/4/202115611.NET Framework: 1114. C# 10 - (13) 단일 파일 내에 적용되는 namespace 선언파일 다운로드1
12827정성태9/4/202115761스크립트: 27. 파이썬 - 웹 페이지 데이터 수집을 위한 scrapy Crawler 사용법 요약
12826정성태9/3/202119464.NET Framework: 1113. C# 10 - (12) 문자열 보간 성능 개선 [1]파일 다운로드1
12825정성태9/3/202115636개발 환경 구성: 603. GoLand - WSL 환경과 연동
12824정성태9/2/202124874오류 유형: 760. 파이썬 tensorflow - Dst tensor is not initialized. 오류 메시지
12823정성태9/2/202114159스크립트: 26. 파이썬 - PyCharm을 이용한 fork 디버그 방법
12822정성태9/1/202119218오류 유형: 759. 파이썬 tensorflow - ValueError: Shapes (...) and (...) are incompatible [2]
12821정성태9/1/202114621.NET Framework: 1112. C# - .NET 6부터 공개된 ISpanFormattable 사용법
12820정성태9/1/202115538VC++: 147. Golang - try/catch에 대응하는 panic/recover [1]파일 다운로드1
12819정성태8/31/202116012.NET Framework: 1111. C# - FormattableString 타입
12818정성태8/31/202113876Windows: 198. 윈도우 - 작업 관리자에서 (tensorflow 등으로 인한) GPU 연산 부하 보는 방법
12817정성태8/31/202117621스크립트: 25. 파이썬 - 윈도우 환경에서 directml을 이용한 tensorflow의 AMD GPU 사용 방법
12816정성태8/30/202123345스크립트: 24. 파이썬 - tensorflow 2.6 NVidia GPU 사용 방법 [2]
12815정성태8/30/202115749개발 환경 구성: 602. WSL 2 - docker-desktop-data, docker-desktop (%LOCALAPPDATA%\Docker\wsl\data\ext4.vhdx) 파일을 다른 디렉터리로 옮기는 방법
12814정성태8/30/202120162.NET Framework: 1110. C# 11 - 인터페이스 내에 정적 추상 메서드 정의 가능 (DIM for Static Members) [2]파일 다운로드1
12813정성태8/29/202117252.NET Framework: 1109. C# 10 - (11) Lambda 개선파일 다운로드1
12812정성태8/28/202116579.NET Framework: 1108. C# 10 - (10) 개선된 #line 지시자
12811정성태8/27/202116462Linux: 44. 윈도우 개발자를 위한 리눅스 fork 동작 방식 설명 (파이썬 코드)
12810정성태8/27/202115454.NET Framework: 1107. .NET Core/5+에서 동적 컴파일한 C# 코드를 (Breakpoint도 활용하며) 디버깅하는 방법 - #line 지시자파일 다운로드1
... 31  32  33  34  35  36  37  38  39  40  41  42  43  [44]  45  ...