


[{"content":"","date":"2026 June 8","externalUrl":null,"permalink":"/en/posts/","section":"Posts","summary":"","title":"Posts","type":"posts"},{"content":"I recently configured dual-stack (IPv4 + IPv6) on my sing-box and hit two pitfalls worth documenting.\nPitfall 1: DNS Strategy Hidden as ipv4_only # IPv6 just wouldn\u0026rsquo;t work. Found \u0026quot;strategy\u0026quot;: \u0026quot;ipv4_only\u0026quot; in DNS config silently discarding all AAAA records. Delete it or change to prefer_ipv6. Easy fix.\nPitfall 2: IPv6 Large Packets Randomly Hang in Chain Proxy # The real nightmare. My topology:\nClient Mihomo (SOCKS5) → Public relay node → Server Nginx (Stream) → sing-box (AnyTLS) → Internet\nAfter enabling dual-stack, test-ipv6.com showed a strange split:\nIPv6 address detected ✅ Small packet 10/10 ✅ Large packet transfer test ❌ (progress bar stuck, timeout) Pages loaded fine, images and video hung. DNS resolved, TLS handshake succeeded — but data flow died as soon as packets got large.\nDebugging # I suspected the relay node, Nginx config, sing-box, even tried anytls-go — all failed.\nAt one point I wondered if some intermediate link in the chain had an MTU problem. But then I realized: the chain proxy already wraps the original request in multiple tunnel layers. The intermediate segments are transporting encapsulated tunnel traffic — MTU at those middle hops simply doesn\u0026rsquo;t matter. The only places that matter are the two endpoints: client egress and server egress.\nSo I ran curl directly on the server for the large packet test URL, and found the server itself couldn\u0026rsquo;t complete it. The problem was at the network layer, not the proxy software.\nThe Smoking Gun # test-ipv6.com\u0026rsquo;s large packet test hits this URL (quote it to prevent shell from misparsing \u0026amp; and ?):\ncurl -6 \u0026#34;https://mtu1280.osaka.test-ipv6.com/ip/?callback=?\u0026amp;size=1600\u0026amp;fill=xxxxxxxx...\u0026amp;testdomain=test-ipv6.com\u0026amp;testname=test_v6mtu\u0026#34; It sends 1600 bytes of padding. If MTU is healthy, you get a JSONP callback with your IPv6 address. If it hangs, there\u0026rsquo;s an MTU black hole.\nRoot Cause: Non-Standard Datacenter MTU # Probed with ping -M do -s and found the exact boundary:\nping -M do -s 1232 ipv6.google.com # works ping -M do -s 1233 ipv6.google.com # fails 1232 + 8 (ICMPv6 header) + 40 (IPv6 header) = 1280.\nThe datacenter\u0026rsquo;s IPv6 path MTU is hard-limited to 1280 bytes. An upstream device has a non-standard MTU, and ICMPv6 Type 2 (Packet Too Big) is either never sent or gets dropped by the proxy layers. PMTUD is completely broken here.\nSince the datacenter doesn\u0026rsquo;t play by the rules — no ICMPv6 Type 2, no path MTU discovery — there\u0026rsquo;s no point relying on protocol negotiation. Forget PMTUD, forget client cooperation. Just enforce the limit at the server\u0026rsquo;s physical layer: packets over 1280 simply don\u0026rsquo;t leave this machine.\nFix: Per-Route MTU Isolation # Goal: different MTU for IPv4 (1500) and IPv6 (1280) on the same NIC.\nWrong Turn: Changing NIC MTU # sudo ip link set dev eth0 mtu 1280 worked, but unnecessarily limited IPv4 to 1280 too.\nFinal Solution: ip -6 route change # Modify the RA-learned default route to limit only IPv6 path MTU:\n# 1. Restore eth0 to 1500 sudo ip link set dev eth0 mtu 1500 # 2. Check current IPv6 default route ip -6 route show default # Output: default via fe80::464c:a8ff:fe08:f27f dev eth0 metric 100 mtu 1500 # 3. Change default route, force IPv6 MTU=1280 sudo ip -6 route change default via fe80::464c:a8ff:fe08:f27f dev eth0 mtu 1280 metric 100 # 4. Change backup route too if exists sudo ip -6 route change default via fe80::464c:a8ff:fe08:f27f dev eth0 metric 1024 mtu 1280 Verify:\nip -6 route show default # default via fe80::... dev eth0 metric 100 mtu 1280 ✅ ip link show eth0 | grep mtu # mtu 1500 ✅ IPv4 unaffected Route-level MTU takes priority over interface MTU. IPv4 uses the default 1500, IPv6 hits the mtu 1280 route entry — kernel handles segmentation automatically. Same NIC, two standards, no conflict.\nSince routes are learned via RA, they reset on reboot. To persist via netplan:\n# /etc/netplan/99-ipv6-mtu.yaml network: version: 2 renderer: networkd ethernets: eth0: mtu: 1500 dhcp4: true dhcp6: true routes: - to: ::/0 via: \u0026#34;fe80::464c:a8ff:fe08:f27f\u0026#34; # replace with your gateway on-link: true mtu: 1280 sudo netplan apply Yet Another Pitfall: RA Resets the Route Every 1800s # After a while, it broke again. ip -6 route showed:\ndefault via fe80::464c:a8ff:fe08:f27f dev eth0 proto ra metric 100 mtu 1500 default via fe80::464c:a8ff:fe08:f27f dev eth0 proto static metric 1024 onlink mtu 1280 The RA route (metric 100) has higher priority than my static route (metric 1024). Kernel always picks lower metric, so traffic still used 1500 MTU. RA refreshes every 1800s, overwriting manual changes.\nFix: netplan with metric 1 # netplan apply triggers RA refresh, so just set metric: 1 in netplan routes — RA uses metric 100, ours is always higher priority:\n# /etc/netplan/99-ipv6-mtu.yaml network: version: 2 renderer: networkd ethernets: eth0: mtu: 1500 dhcp4: true dhcp6: true routes: - to: ::/0 via: \u0026#34;fe80::464c:a8ff:fe08:f27f\u0026#34; # replace with your gateway on-link: true metric: 1 mtu: 1280 sudo netplan apply No cron needed. Netplan handles it all in one shot.\nTL;DR # IPv6 not working → check DNS strategy isn\u0026rsquo;t ipv4_only IPv6 small packets work, large packets hang → test on the server directly with the test-ipv6.com curl URL. Use ping -M do -s to find the MTU boundary, then ip -6 route change for per-route MTU isolation — IPv4 at 1500, IPv6 at 1280 ","date":"2026 June 8","externalUrl":null,"permalink":"/en/posts/singbox_dualstack/","section":"Posts","summary":"","title":"Singbox Dual-Stack: Two Pitfalls","type":"posts"},{"content":"","date":"2026 June 8","externalUrl":null,"permalink":"/en/","section":"soloopooo's blog - reset version","summary":"","title":"soloopooo's blog - reset version","type":"page"},{"content":" Introduction # I believe many people have heard the \u0026ldquo;cat call\u0026rdquo; from the \u0026ldquo;ancient times\u0026rdquo; of dial-up connections. Back then, people were still using telephone lines to connect to the internet, with speeds up to only 57.6Kbps. The phrase \u0026ldquo;too many pictures kill the cat\u0026rdquo; also originated from that period.\nRecently, while surfing the internet, I saw some content creators on Bilibili using telephone switches and physical modems to enable dial-up internet on Windows 11, even achieving interconnection between two machines and super terminal functionality. I was wondering if it was possible to simulate these functions using software alone, without physical hardware, to achieve interconnection and dial-up internet?\nI indeed found such an open-source software: atduck\nAtduck simulates a Hayes-compatible modem.\nDeploying atduck # In 2019, there was a blog post that detailed the steps for using atduck for dial-up internet.\nATduck itself is easy to install, but it relies on the Perl IO-Pty module and the Slirp software. Slirp can simulate a SLIP or PPP protocol server on a TTY, while ATduck only implements the modem part, leaving the networking part to Slirp.\nHowever, for obvious reasons, Slirp is rarely used now. It is not available in the Arch Linux software repository, not even in AUR. According to the Slirp page on Wikipedia, part of Slirp\u0026rsquo;s maintenance is handled by Debian maintainers.\nHere is the Dockerfile he used:\nRUN PKG_INSTALL(git slirp perl libio-pty-perl tini) \\ \u0026amp;\u0026amp; git clone https://github.com/nandhp/atduck.git \\ \u0026amp;\u0026amp; ln -sf /usr/bin/slirp /atduck/slirp-nandhp-patch \\ \u0026amp;\u0026amp; PKG_UNINSTALL(git) \\ \u0026amp;\u0026amp; FINAL_CLEANUP() WORKDIR /atduck ENTRYPOINT [\u0026#34;tini\u0026#34;, \u0026#34;/atduck/atduck\u0026#34;, \u0026#34;--\u0026#34;, \u0026#34;-l\u0026#34;, \u0026#34;0.0.0.0:5555\u0026#34;] Or you can directly use his image: xddxdd/atduck\nIf you are using VirtualBox, you can directly follow the blog instructions to add a serial port and use it.\nHowever, for VMware virtual machines, this Dockerfile cannot be used: on one hand, it specifies that atduck listens on a TCP port, while VMware only supports Unix Socket communication for serial ports. So we need to make a slight modification:\nMethod 1: Directly modify the existing image startup command\nFROM xddxdd/atduck ENTRYPOINT [\u0026#34;tini\u0026#34;,\u0026#34;/atduck/atduck\u0026#34;,\u0026#34;--\u0026#34;,\u0026#34;-l\u0026#34;,\u0026#34;/tmp/modem\u0026#34;] Method 2: Directly change the ENTRYPOINT in the DockerFile to ENTRYPOINT [\u0026quot;tini\u0026quot;,\u0026quot;/atduck/atduck\u0026quot;,\u0026quot;--\u0026quot;,\u0026quot;-l\u0026quot;,\u0026quot;/tmp/modem\u0026quot;].\nRUN PKG_INSTALL(git slirp perl libio-pty-perl tini) \\ \u0026amp;\u0026amp; git clone https://github.com/nandhp/atduck.git \\ \u0026amp;\u0026amp; ln -sf /usr/bin/slirp /atduck/slirp-nandhp-patch \\ \u0026amp;\u0026amp; PKG_UNINSTALL(git) \\ \u0026amp;\u0026amp; FINAL_CLEANUP() WORKDIR /atduck ENTRYPOINT [\u0026#34;tini\u0026#34;,\u0026#34;/atduck/atduck\u0026#34;,\u0026#34;--\u0026#34;,\u0026#34;-l\u0026#34;,\u0026#34;/tmp/modem\u0026#34;] Then compile and run.\ndocker build -t atduck1 . You can also use docker compose directly:\nservices: atduck: image: atduck1 # Change to your compiled image name container_name: atduck restart: always volumes: - /tmp:/tmp # If you are using Unix Socket as shown, add this. Otherwise, as shown in the original blog, just add port listening. Achieving Dial-Up Internet # We are using VMware here.\nAccording to the ENTRYPOINT directive in the previous section, we created a Unix Socket at the /tmp/modem location.\nHowever, first, we need to set its permissions, and this needs to be set every time atduck docker starts; otherwise, VMware, as a normal user, cannot read and write this Socket and will report insufficient permissions.\nNote! If you are not starting the atduck container for the first time, please be sure to delete /tmp/modem before starting; otherwise, there may be listening issues.\nsudo rm /tmp/modem # These two commands must be executed after starting the container, username is your username sudo chown username /tmp/modem sudo chgrp username /tmp/modem Then, in the virtual machine settings where we need to dial up to the internet, add a serial port device:\nAdd Serial Device Set it as shown in the image, choose to use socket (named pipe), and set From Client to An Application.\nSet Serial Start the virtual machine, and in \u0026ldquo;Control Panel -\u0026gt; Printers and Other Hardware -\u0026gt; Phone and Modem Options\u0026rdquo;, install the modem (you can directly install the standard 56000bps modem under Windows XP, while under Windows 2000, you need to install the Hayes modem).\nModem Installed Add a new dial-up connection using the new connection wizard:\nChoose to connect to the Internet Choose to manually set up my connection Choose to connect using a dial-up modem Enter a connection name, any name will do, this name is your connection name Enter the phone number: 5555 refer to here Leave username and password blank, no need to enter You can choose to add a shortcut Then it is created, and you can connect to enjoy surfing the internet!\nConnection Successful What Phone Number to Choose? # According to the atduck usage instructions DIALING PLAN:\nThe following dialing plan is configured by default.\n5550 Connect to a shell on the host\n5555 SLiRP (PPP mode)\n5556 SLiRP (SLIP mode)\n6xxx Call another emulated modem by serial number. For example, dialing 6006 will connect you to the modem with serial number 6. To determine your modem\u0026rsquo;s serial number use AT+GSN. Note that serial numbers are not fixed between sessions.\nWe can see that to achieve dial-up internet, the virtual machine needs to dial 5555 or 5556, we choose the PPP mode used by Windows by default, which is 5555.\nAchieving Interconnection Between Two Machines # Note! Before proceeding with the next operations, please disconnect your dial-up connection, as it will \u0026ldquo;occupy the line\u0026rdquo;. Since the dial-up connection is successful, we naturally need to experience the joy of sending \u0026ldquo;large files\u0026rdquo; while chatting with friends.\nWe need to use the super terminal, which is available in the original images from Windows 3.1 to Windows XP, usually found in Accessories - Communications.\nWe use a Windows 2000 virtual machine to interconnect with our Windows XP.\nWe need to let one atduck instance listen on two Unix Sockets simultaneously, and we can modify the ENTRYPOINT as follows:\nENTRYPOINT [\u0026#34;tini\u0026#34;,\u0026#34;/atduck/atduck\u0026#34;,\u0026#34;--\u0026#34;,\u0026#34;-l\u0026#34;,\u0026#34;/tmp/modem\u0026#34;,\u0026#34;-l\u0026#34;,\u0026#34;/tmp/modem2k\u0026#34;] /tmp/modem2k is the Socket that our Windows 2000 virtual machine will use.\nOpen a new terminal, still handle the permission issues as we mentioned above.\nOpen the Windows 2000 virtual machine and install the modem.\nAccording to what phone number to choose:\n6xxx Call another emulated modem by serial number. For example, dialing 6006 will connect you to the modem with serial number 6. To determine your modem\u0026rsquo;s serial number use AT+GSN. Note that serial numbers are not fixed between sessions.\nWe know that to achieve interconnection between two machines, we need to dial 6xxx. So how much should we dial?\nWe need to use putty for direct serial communication, download putty version 0.61, which can run on Windows 2000 and Windows XP.\nOpen putty, select Serial connection to COM1\nClick connect and enter AT+GSN:\nTip! If you use docker logs -f atduck, you can see the AT command logs of atduck, allowing you to view your interactions in real-time.\nUsing AT Command to Check Serial Number Seeing the reply 2 in the image indicates that the phone number of this computer is 6002.\nWe open the super terminal and wait for the call:\nWaiting for Call Open Windows 2000 and dial 6002, be careful not to include the area code:\nNote! The modem in the image is incorrect; you need to install a Hayes-compatible modem, and Windows 2000 can choose the standard 56000 V90. Calling Then\u0026hellip; it connected!\nInterconnection Between Two Machines Send a \u0026ldquo;large file\u0026rdquo; using the transfer function:\nSending File\u0026hellip; Conclusion # This program can do more things, but I haven\u0026rsquo;t tried much, so I can only write this much.\n","date":"2026 January 4","externalUrl":null,"permalink":"/en/posts/using_atduck/","section":"Posts","summary":"","title":"Using atduck to Simulate Dial-Up Internet and Interconnect Two Machines","type":"posts"},{"content":"This blog is currently deployed to both Pages. See Cloudflare Pages, Github Pages\nFor well-known reasons, Cloudflare Pages can often be faster than GitHub Pages, thanks to Cloudflare\u0026rsquo;s global CDN network (except for some regions).\nThanks to the Pages feature in the Cloudflare dashboard, it\u0026rsquo;s easy to deploy an existing repository.\nThe source repository for this blog is here:\nPublishing to GitHub Pages and Cloudflare Pages is very simple:\nWith powerful and free GitHub Actions, you can use the official Actions to build to the GitHub Pages environment. Similarly, Cloudflare can perform automatic builds and will automatically rebuild and publish to Cloudflare\u0026rsquo;s global network whenever the git repository is updated.\nCloudflare Pages build page Readers can write Workflows according to their static site\u0026rsquo;s build environment; once written, they will automatically build and publish to Pages.\nNote! Please ensure that the custom domains for GitHub Pages and Cloudflare Pages are different. Note! Because Cloudflare Pages\u0026rsquo; default top-level domain pages.dev can be affected by SNI blocking, be sure to add a custom domain. ","date":"2025 December 22","externalUrl":null,"permalink":"/en/posts/gpages_and_cfpages/","section":"Posts","summary":"","title":"Deploying a Static Site with Both GitHub Pages and Cloudflare Pages","type":"posts"},{"content":" I Was Reborn\u0026hellip; # Because the previous blog content was lost, I\u0026rsquo;ve been reborn\u0026hellip;\nPowered by Hugo \u0026amp; Blowfish.\nMultilingual support has been added. The source language is Chinese, and translations may be done by me or by AI.\n","date":"2025 December 21","externalUrl":null,"permalink":"/en/posts/rel/","section":"Posts","summary":"","title":"I Was Reborn...","type":"posts"},{"content":"","date":"2025 December 21","externalUrl":null,"permalink":"/en/links/","section":"Links","summary":"","title":"Links","type":"links"},{"content":"Here\u0026rsquo;re some external links：\n心水湛清 # # Kamipoi # # Trave11er aka Maxizero # # ","date":"2025 December 21","externalUrl":null,"permalink":"/en/links/links/","section":"Links","summary":"","title":"Links","type":"links"},{"content":"","externalUrl":null,"permalink":"/en/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/en/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","externalUrl":null,"permalink":"/en/series/","section":"Series","summary":"","title":"Series","type":"series"},{"content":"","externalUrl":null,"permalink":"/en/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"}]