投稿者「みむら」のアーカイブ

Hyper-V の第2世代で Linux の起動ログを追いかける

どうも、みむらです。

Hyper-V 上で Linux を動かすかとやっていたのですが
内部で起動がコケてしまって、その追跡にとても時間が掛かったのでそのメモです。

1.COM ポートを生やす

PowerShell で “Set-VMComPort” コマンドを使うことで生やせます。

Set-VMComPort (Hyper-V) | Microsoft Learn
https://learn.microsoft.com/en-us/powershell/module/hyper-v/set-vmcomport

Set-VMComPort -VMName "Gentoo Linux" -Number 1 -Path \.\pipe\vm_debugcom

-VMName は VM 名、 -Number は 1を最小値として ttyS0 に当たっていきます。

2. COM を見る

手っ取り早いのが PuTTY です。
管理者権限で起動した PuTTY で、名前付きパイプの文字列をそのまま入力すると繋いでくれます。

その後、起動オプションに CONSOLE の設定を入れて起動します。
サンプルとしては下記のような感じです:

linux /vmlinuz-6.5.4-gentoo console=ttyS0,9600
boot

3.ログを眺める

コンソールの内容が PuTTY 側に流れてきます。

今回の場合はどうやら “noxsave” を入れれば治るらしいのですが、
気持ち悪いので原因調査をしてみます。


“XSAVE has to be disabled as it is not supported by this module” ということで、
これで検索を掛けて見ると、下記のパッチが見つかります。

https://lwn.net/ml/linux-kernel/1678386957-18016-3-git-send-email-ssengar@linux.microsoft.com/

コードを抜粋するとまさにこんな感じ。

+++ b/arch/x86/hyperv/hv_vtl.c

+static int __init hv_vtl_early_init(void)
+{
+	/*
+	 * `boot_cpu_has` returns the runtime feature support,
+	 * and here is the earliest it can be used.
+	 */
+	if (cpu_feature_enabled(X86_FEATURE_XSAVE))
+		panic("XSAVE has to be disabled as it is not supported by this module.\n"
+			  "Please add 'noxsave' to the kernel command line.\n");
+
+	real_mode_header = &hv_vtl_real_mode_header;
+	apic->wakeup_secondary_cpu_64 = hv_vtl_wakeup_secondary_cpu;
+
+	return 0;
+}

調べてみると、 “Enable Linux to boot in VTL context” という項目を発見。

切ってみたところ、正常に起動しました。


というわけで、第2世代でコンソールを生やして中身を追いかける話・・でした!
エラーメッセージを見れば一撃なんですけど、ここにたどり着くまでに数時間溶かしてしまったので、まだまだ精進します、、、💦

ElasticSearch OSS / OpenSearch の Beats が “runtime/cgo: pthread_create failed: operation not permitted” で落ちるのを直す

どうも、みむらです。

某所の監視に Wazuh ( https://wazuh.com/ )をよく使っているのですが
内部的に ElasticSearch OSS 7.10.2 を使っているため、新しすぎる OS 上で動かそうとすると filebeat が下記のように落ちてしまうことがあります

runtime/cgo: pthread_create failed: Operation not permitted

もちろん、この問題は新しいバージョンの Beats では解決されているのですが
他方で 7.13 以降の Beats は OpenSearch 1.x や Elasticsearch OSS などで使えないという問題があります。

https://opensearch.org/docs/latest/clients/agents-and-ingestion-tools/index/

この問題については、下記のコメントにあるように elasticsearch 側もアップデートすることが推奨されていますので、公式に解決される可能性は低そうです。

https://github.com/elastic/beats/pull/26305#issuecomment-863472649

枯れたものを使え、公式でサポートしていないディストリビューションを使うな、というのは一理あるのですが、その方法で回避するのは面白くないですので、修正含めてやってみました。

(wazuh のサポートで解決している例が見当たらなかったこともあり、最初だけ英語で併記します。)

注意:

修正は自己責任でお願いします。本番用環境に対して独自ビルドを行ったものを適用したことにより問題が発生しても当方では責任を負えません。


落ちる原因 (Cause):

“clone3” のシステムコールが seccomp の許可リストに登録されていないため。
en : The systemcall “clone3” is not allowed by seccomp.

glibc 2.34 以降において、 pthread_create() を呼び出す際に clone3 システムコールが用いられるようになったことが原因となっています。

修正方法 (Solution):

libbeat/common/seccomp 以下の “policy_linux_386.go” と “policy_linux_amd64.go” に “clone3” を追記する
en: Add “clone3” to policy_linux_386.go and policy_linux_amd64.go under libbeat/common/seccomp.

具体的な追加内容については、まさしく当該する patch がありますのでこれに従います。
https://github.com/elastic/beats/commit/82507fda20bee46cee4808d388a0c809dd01ff13

また glibc 2.35 以降では “rseq” システムコールも用いるそうですので
こちらも併せて対応しておくとよいと思います。
en: It is a recommend to also add “rseq” to policy_linux_386.go and policy_linux_amd64.go under libbeat/common/seccomp, due to the syscall is used glibc >= 2.35.

https://github.com/elastic/beats/commit/f02fa32e0a37d6529983e2181b80bf62e4a16b41


実際にやってみる

実際に上記のパッチを当てて問題が解決するか確認してみました。

確認環境 : Fedora 36 x86_64

続きを読む

CentOS から Fedora に直接アップグレードしてみる

どうも、みむらです。

気がついたら 2022 年になっていましたし、オリンピックも終わっていました。
年が進むのは早いものですね。

同じく年が早いと気づかされたものとして、CentOS Linux 8 のサポート切れがあります。

CentOS Stream 8 にアップグレードしましょう、という話もありますが、
“Stream” は RHEL の “Nightly” の立ち位置になっていますし、
今までのRHEL 互換を求めるなら、Alma Linux Rocky Linux に切替えていくのが正しいように見えます。

Fedora/CentOS Stream/CentOS/RHELの関係性 – 赤帽エンジニアリングブログ
https://rheb.hatenablog.com/entry/202007-fedora-distribution

GitHub の CentOS などから Alma Linux に移行するためのツール (Almalinux-deploy):
https://github.com/AlmaLinux/almalinux-deploy

上記の移行ツールの使用について日本語で書かれた記事:
https://www.server-memo.net/memo/centos8_to_almalinux.html

他方、 CentOS Stream 9 からミラーが変わるようで、
執筆時点 (2022/01/17) では日本のサーバがなく寂しい所でもあります。

CentOS Stream 9 Mirror list:
https://admin.fedoraproject.org/mirrormanager/mirrors/CentOS

今実験用に稼働させているサーバのひとつが CentOS なのですが、
実験用ですし CentOS Stream が “Nightly” の立場に変わるのであれば、
更に上流の Fedora にアップグレード出来るのではないか・・と試してみました。


注意:
下記の手法を試したことにより生じた損害や被害などは当方では全く関知しません。
また、公式ではない方法かつ危険が伴う可能性もあるため、バックアップ等を取り、リスクを取ることが出来る環境でのみお試しください。


続きを読む

Jumbo Frame + WinServer 2019 の Hyper-V の通信は妙に遅い。

どうもみむらです。
最近梅雨に入ったのか夏に入ったのか、色々パッとしなくて困る日々が続いております。

先日 Windows Server 2019 (もしくは Hyper-V Server 2019) を Jumbo Frame 環境下で用いると、通信が遅くなるということが分かりましたのでメモがてら。
同じような問題に遭遇した方の一助のとなればありがたいです。


事象:

Windows Server 2019 上の Hyper-V に展開された VM において
RSC (Receive Side Coalescing, Linux では Large Receive Offload で知られている) が有効
かつ VM が Jumbo Frame の環境において、VMから見て受信方向の通信が遅くなる。

注1:Windows Server 2019 では RSC は既定で有効になっています。
注2:MTU が 2850 bytes を上回る ( >2850 ) 場合に速度低下が発生するようです。

原因:

VMSwitch において Reassemble されたパケットが VMに届かなくなる。

回避方法:

RSC を無効にするか MTU を 1500 にする

方法1:ゲスト VM の MTU を 1500 にする。
 例) ip l set mtu 1500 dev eth0

方法2:ホスト側でRSC 機能を VMSwitch 単位で無効にする
 例)Set-VMSwitch (Switch名) -EnableSoftwareRsc $false

方法3:ゲストVM において ethtool 等を用いて “large-receive-offload” を off にする
 Linux) ethtool -K eth0 large-receive-offload off
 Windows) Set-NetAdapterAdvancedProperty “*” -DisplayName “Recv Segment Coalescing” -RegistryValue 0


パケットの気持ちになってみる

最近パケットの気持ちになる、が一部界隈で有名ですので「なってみよう」と思います。

ざっくりと構成は下記の通り。
Hyper-V サーバ内に立てられた VM-01 との通信について Hyper-V サーバの前段のミラー (CAP-01) と VMSwitch のミラー (CAP-02) で通信をキャプチャして挙動を確認しよう、という構成になっています。

なお、各キャプチャは同タイミングでのものではありません。ご了承ください


VM-01 での curl での速度比較

事象を確認するためにまずは curl で適当な通信を発生させて速度を見てみます。

MTU 1500 の場合は Average Speed が 10.7M となっているのに対し
MTU 9000 の場合は Average Speed が 44024 となっています。


MTU 9000 の時の VM-01 のキャプチャ

上記のようになります。

No. 12 において Seq=141, Ack=1409 をサーバに対して返答していますが
その次にやってきたパケット (No.13) は Seq=16897 になっています。
(通常は直前の Ack と同じ番号の Seq が返ってきます)

そのため No.14 において Ack=1409 を再度送信され、
サーバ側からは No.16 において Seq=1409 の返答(再送)が起きてしまっています。


MTU9000 の時の CAP-02 のキャプチャ

上記のようになります。
No. 13 までは先ほどの VM-01 と同じような流れになっています。

ですが No.14 付近から 1474 bytes ではないパケットが流れはじめます。
またそれを境にして、 Ack と Seq の関係が壊れはじめるのも確認が出来ます。

たとえば No.14 は Seq=1409 として 2882 bytes の通信が行われていますが
その後の ACK (No.16) では Ack=1409 として返答が行われていることが分かり、
通信が正常に行われていないことがここから読み取れます。

またこの 1475 bytes でないパケットについては先述の VM-01 のキャプチャにおいては確認出来ず、その後発生した No.25 の Ack に対する No.26 の 1474 bytes の通信が行われて初めて VM-01 側にパケットが到達しているように見受けられます。


MTU9000 の時の CAP-01 のキャプチャ

上記のようになります。

インターネットを介して No.10 ~ No.19 に掛けて勢いよく通信が行われていますが
その後 No.20 において Ack=1 が返され、No.23 において再送が行われています。

この間 Seq は 12673 まで増えており、この No.19 に当該するパケットは先述の CAP-02 においても No.21 として観測できているように見受けられます。


以上の事象、また CAP-02 においてパケットをとり続けると、 reassemble されたパケットが送信された後に再送要求が発生していること、そして RSC を Disable にするとこれらの事象が解決することから RSC (LRO) が原因と判断しました。

また繰り返しではありますが、
Windows Server 2022 (Preview) や Windows 10 (21H1) では発生しないことを確認していますので Windows Server 2019 特有の問題 ( 1809 ベースの Hyper-V 特有? ) と判断しています。


番外:ドライバベースで治せないかやってみる

Linux のドライバは自由に修正したりして実験できますので、
これで何か出来ないかやってみます。

1.そもそも機能を切る:

netvsc の 603 行目に下記のような記述があります
https://github.com/torvalds/linux/blob/master/drivers/net/hyperv/netvsc.c#L603

/* Negotiate NVSP protocol version */
static int negotiate_nvsp_ver(struct hv_device *device,
			      struct netvsc_device *net_device,
			      struct nvsp_message *init_packet,
			      u32 nvsp_ver)
{

/// 省略 ///

	if (nvsp_ver >= NVSP_PROTOCOL_VERSION_61)
		init_packet->msg.v2_msg.send_ndis_config.capability.rsc = 1;

/// 省略 ///

	return ret;
}

この nvsp_ver で Windows Server 2019 とそれ以降の区別を試みましたが
共に NVSP_PROTOCOL_VERSION_61 (0x60001) が返るため、区別は出来ませんでした。

もちろんですが、”init_packet->msg.v2_msg.send_ndis_config.capability.rsc = 0;” とすると RSC の機能が恒久的に無効になります。


2.別パラメータから値を推測する等で値を修正する:

こちらですが、そもそも VMBus 経由での VMQ の割込が来ないため
修正は難しいという形になりました。

Hyper-V のネットワーク通信は下記のようなアーキテクチャになっています。

vmq コンポーネント

引用元 : https://docs.microsoft.com/ja-jp/windows-hardware/drivers/network/vmq-components

親(ホスト)が持つ NetVSP (VMSwitch) に対して VMBus 経由で接続するアーキテクチャになっており、Linux の netvsc ドライバにおいても 1665 行目付近でその接続が行われていることが伺えます。

struct netvsc_device *netvsc_device_add(struct hv_device *device,
				const struct netvsc_device_info *device_info)
{

/// 省略 ///

	/* Enable NAPI handler before init callbacks */
	netif_napi_add(ndev, &net_device->chan_table[0].napi,
		       netvsc_poll, NAPI_POLL_WEIGHT);

	/* Open the channel */
	device->channel->rqstor_size = netvsc_rqstor_size(netvsc_ring_bytes);
	ret = vmbus_open(device->channel, netvsc_ring_bytes,
			 netvsc_ring_bytes,  NULL, 0,
			 netvsc_channel_cb, net_device->chan_table);

	if (ret != 0) {
		netdev_err(ndev, "unable to open channel: %d\n", ret);
		goto cleanup;
	}

	/* Channel is opened */
	netdev_dbg(ndev, "hv_netvsc channel opened successfully\n");

	napi_enable(&net_device->chan_table[0].napi);

	/* Connect with the NetVsp */
	ret = netvsc_connect_vsp(device, net_device, device_info);
	if (ret != 0) {
		netdev_err(ndev,
			"unable to connect to NetVSP - %d\n", ret);
		goto close;
	}

/// 省略 ///

}

https://github.com/torvalds/linux/blob/9d31d2338950293ec19d9b095fbaa9030899dcb4/drivers/net/hyperv/netvsc.c#L1648


試しに netvsc_receive 関数を下記のように編集してみると下記のような出力が得られました。

static int netvsc_receive(struct net_device *ndev,
			  struct netvsc_device *net_device,
			  struct netvsc_channel *nvchan,
			  const struct vmpacket_descriptor *desc)
{
	struct net_device_context *net_device_ctx = netdev_priv(ndev);
	struct vmbus_channel *channel = nvchan->channel;
	const struct vmtransfer_page_packet_header *vmxferpage_packet
		= container_of(desc, const struct vmtransfer_page_packet_header, d);
	const struct nvsp_message *nvsp = hv_pkt_data(desc);
	u32 msglen = hv_pkt_datalen(desc);
	u16 q_idx = channel->offermsg.offer.sub_channel_index;
	char *recv_buf = net_device->recv_buf;
	u32 status = NVSP_STAT_SUCCESS;
	int i;
	int count = 0;

  // 下記行を追記
	netif_info(netdevice_ctx, rx_err, ndev, "BUF-SIZE : %u, SEC-SIZE : %u, RSC-PKTLEN %u\n",
    net_device->recv_buf_size, net_device->recv_section_size,
    nvchan->rsc.pktlen);

	/* Ensure packet is big enough to read header fields */
	if (msglen < sizeof(struct nvsp_message_header)) {
		netif_err(net_device_ctx, rx_err, ndev,
			  "invalid nvsp header, length too small: %u\n",
			  msglen);
		return 0;
	}

/// 以下省略 ///

BUF-SIZE が Window Size にちょっと足したもの、SEC-SIZE が MTU にちょっと足したものの値になり、 RSC-PKTLEN がフレームサイズと同じ値を指し示すようです。

RSC-PKTLEN の値は 1474 を示しており、冒頭のパケットキャプチャと同じような感じになっていることが読み取れます。

なお、同じ VM を同じ設定で Windows 10 の上に構築した場合は下記のようになります。

RSC-PKTLEN が十分に大きな値になっており、 RSC にて reassemble されたパケットが受信出来ていることが分かります。

ドライバを追いかけてみたのですが、正しい値が別パラメータに入っていることなどはなく、また NetVSP の割込が来ないため修正は難しいと考えられました。


まとめ

Windows Server 2019 の Hyper-V を使用して VM を作成する場合は

・MTU を 1500 以下に設定して RSO が正常に機能するようにして使う
・Jumbo Frame を有効にしたい場合は RSO を Disable にする

のどちらかで利用しないと、落とし穴があるという話です。

執筆時点の最新版である “10.0.17763.1999” でもこの事象は発生していますので、
お気をつけくださいませ。


P.S.

割と海外のフォーラムだと “RSO を無効にしたら良くなった!” 的なのはちらほら報告されているみたいですね。。修正されたらいいなとぼんやり思ってます。。

https://social.technet.microsoft.com/Forums/en-US/8aa6a88c-ffc8-4ede-abfc-42e746ff5996/windows-server-2019-hyperv-guest-on-windows-server-2019-hyperv-host?forum=winserverhyperv

https://www.doitfixit.com/blog/2020/01/15/slow-network-speed-with-hyper-v-virtual-machines-on-windows-server-server-2019/

VPN 越しに DLNA 経由で NAS に触る (SSDP Proxy)

どうもみむらです。

VPN 越しに NAS のコンテンツに、しかも DLNA 対応アプリ経由で触りたいと思ったことはありませんか。

数ヶ月前に NAS を購入して、速攻で静音化の記事 ( ASUSTOR DataSync Center の HDD アクセスを静かにする ) を書いたりしていたのですが、DLNA 経由で音楽ファイルに触れるようにしてみた所これが大変使いやすく、ぜひ VPN 越しにもこれを享受したい・・と。。

DLNA は UPnP を用いており、その機器発見に SSDP (1900/udp) を用いているのですが、早い話が VPN 越しにブロードキャストが上手く動作しないので、これを改善してみようというそういう話です。


1.サーバから VPN 感を感じ取れないようにする

DLNA は同一ネットワーク上での動作を想定しているものになります。
他のネットワークからアクセスしようとすると上手く動いてくれないケースがいくつか見られましたので、下記のようにしてサーバからの 「VPN 感」を少し消してみます。

NAS 側には “192.168.1.0/24” のネットワークに属しているように見せ、VPN 用に “192.168.1.224/28” のネットワークを用意しておきます。

VPN サーバには Proxy ARP の設定を入れて、192.168.1.0/24 のネットワーク上からも 192.168.1.224/28 の端末の MAC アドレスの解決 (といっても VPN サーバが指し示されるだけ)が出来るようになります。

なお、Proxy ARP については、こちらのサイトが詳しいですので参照ください:
Proxy ARP(プロキシARP)とは / ネットワークエンジニアとして
https://www.infraexpert.com/study/gateway.htm


具体的な設定方法としては
“/proc/sys/net/ipv4/conf/<device>/proxy_arp” を 1 に書き換えるか
systemd-networkd の “[Network]” セクションに “IPv4ProxyARP=yes” とする方法が
簡単かなと思います。

設定する際は、“192.168.1.0/24” の方に ProxyARP の設定を適用する形になります。

systemd-networkd の設定例)

# cat /etc/systemd/network/eth0.network
[Match]
Name=eth0

[Network]
Address=192.168.1.240/24
Gateway=192.168.1.254
IPv4ProxyARP=yes

# cat /etc/systemd/network/eth1.network
[Match]
Name=eth1

[Network]
Address=192.168.1.238/28

[Link]
MTUBytes=1200

2. SSDP をプロキシする

探索プロトコルの通信を転送するようにしてみます。
転送しないと探索が VPN のネットワーク内で閉じてしまい、本来の機器まで届かなくなってしまいます。

SSDP の中身は概ね下記のような HTTP ベースの通信になっています。

# リクエストの例。(クライアントからサーバに対して送る)
# 239.255.255.250:1900/UDP に対して送出される
---
M-SEARCH * HTTP/1.1
MX: 5
ST: urn:schemas-upnp-org:device:MediaServer:1
MAN: "ssdp:discover"
User-Agent: UPnP/1.0
Host: 239.255.255.250:1900
Connection: close

---

# 応答例。
# サーバからリクエストを行ったクライアントのポートに対してユニキャストで応答が入る
---
HTTP/1.1 200 OK
Cache-Control: max-age=1800
EXT:
Location: http://192.168.1.22:55247/dms
Server: Linux 4.0.0 UPnP/1.0
ST: urn:schemas-upnp-org:device:MediaServer:1
USN: uuid:xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxxx::urn:schemas-upnp-org:device:MediaServer:1
Date: Sun, 16 May 2021 19:42:50 GMT

---

見ていただくとわかるように、応答の中にユニキャストアドレスが含まれているため、発見処理のみプロキシしてあげれば後は1対1での通信となります。

ということで下記のようなプログラムを書いてみました

#!/bin/python3

#
# SSDP (DLNA の探索プロトコル) を吸って吐くプロキシ。
# Wireguard のように純粋にbridgeできないネットワーク間で使うと幸せになれる気がします。
# (使ったことでネットワークがダウン等しても保証は出来ませんので、自己責任でどうぞ。)
#
# Author : Satoshi Mimura (@mimura1133)
#

import socket
from contextlib import closing

def main():
        multicast_group = '239.255.255.250'
        src_adapter_ip = '' # DLNA クライアントがいるネットワークの IP アドレスを指定
        dst_adapter_ip = '' # DLNA サーバがいるネットワークの IP アドレスを指定
        port = 1900
        timeout = 5.0

        with closing(socket.socket(socket.AF_INET,socket.SOCK_DGRAM)) as src_sock:
            src_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
            src_sock.bind(('',port))
            src_sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP,
                                  socket.inet_aton(multicast_group) + socket.inet_aton(src_adapter_ip))

            while True:
                request_data, client_addr = src_sock.recvfrom(4096)
                with closing(socket.socket(socket.AF_INET,socket.SOCK_DGRAM)) as dst_sock:
                    dst_sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton(dst_adapter_ip))
                    dst_sock.settimeout(timeout)
                    dst_sock.sendto(request_data,(multicast_group,port))
                    print("\033[31m [FORWARDED:REQUEST]\033[0m {} -> {}".format(client_addr,(multicast_group,port)))
                    print(request_data)
                    while True:
                        try:
                            response_data, server_addr = dst_sock.recvfrom(4096)
                            src_sock.sendto(response_data,client_addr)
                            print("\033[32m [FORWARDED:RESPONSE]\033[0m {} -> {}".format(server_addr,client_addr))
                            print(response_data)

                        except Exception:
                            break
        return

if __name__ == '__main__':
    main()

動かしてみた結果は下図の通り。中々上手く動いてます。

今回の検証には iPhone 側に OPlayer Lite を入れて検証しています。

3.永続化する

毎回手動で起動するのは筋がよくありませんので、systemd のサービスにしてしまいましょう。

まずは、先ほどの Proxy をするプログラムからデバッグ用の print 文を消去したものを準備します。

#!/bin/python3

#
# SSDP (DLNA の探索プロトコル) を吸って吐くプロキシ。
# Wireguard のように純粋にbridgeできないネットワーク間で使うと幸せになれる気がします。
# (使ったことでネットワークがダウン等しても保証は出来ませんので、自己責任でどうぞ。)
#
# Author : Satoshi Mimura (@mimura1133)
#

import socket
from contextlib import closing

def main():
        multicast_group = '239.255.255.250'
        src_adapter_ip = '' # DLNA クライアントがいるネットワークの IP アドレスを指定
        dst_adapter_ip = '' # DLNA サーバがいるネットワークの IP アドレスを指定
        port = 1900
        timeout = 5.0

        with closing(socket.socket(socket.AF_INET,socket.SOCK_DGRAM)) as src_sock:
            src_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
            src_sock.bind(('',port))
            src_sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP,
                                  socket.inet_aton(multicast_group) + socket.inet_aton(src_adapter_ip))

            while True:
                request_data, client_addr = src_sock.recvfrom(4096)
                with closing(socket.socket(socket.AF_INET,socket.SOCK_DGRAM)) as dst_sock:
                    dst_sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton(dst_adapter_ip))
                    dst_sock.settimeout(timeout)
                    dst_sock.sendto(request_data,(multicast_group,port))
                    while True:
                        try:
                            response_data, server_addr = dst_sock.recvfrom(4096)
                            src_sock.sendto(response_data,client_addr)

                        except Exception:
                            break
        return

if __name__ == '__main__':
    main()

上記を /usr/local/sbin/proxy_dlna.py として保存しておきます。

保存が完了したら下記のように systemd のサービスを作成します。
今回は下記の内容を /etc/systemd/system/proxy_dlna.service として作成しました。

[Unit]
Description=Launch DLNA Proxy (/usr/local/sbin/proxy_dlna.py)
# After=wg-quick@wg0.service  #WireGuard を使っている場合はこうしておくと WireGuard が起動した後に起動するようになります。

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/proxy_dlna.py

[Install]
WantedBy=multi-user.target

保存が完了しましたら systemctl enable proxy_dlna 等で有効にすれば完了です。


手順は以上となります。

ただ上記のようなプロキシで全て動作するかというとそうではなく、特に DTCP-IP 等を用いるシステムの場合は「TTL は 3ホップ以下」「RTT は 7ms 以下」という制約が存在するため実現するのが大変難しい状況になっています。

もしそのような場合については NTT の NGN 網の特殊性を用いて回避しているケースや、L2TPv3 などを上手く組み合わせて回避しているケースがネット上にいくつか転がっていますのでそちらを用いるのが良いかなと思われます。

とはいえ、当方環境ではこれで VPN ライフがかなり改善されましたので、もしよろしければ参考にしていただけたらと!