为什么 Bridge 需要 IP 地址

并是不一定需要,但当主机需要访问连在 vmbr 上的虚拟机 / 虚拟机需要直接连接主机所处的网络时就有必要。

Linux bridge: MAC addresses and dynamic ports « \1 这里提到了个非常重要的点,Bridge 会选择连接设备中 MAC 地址最小的作为自己的 MAC 地址,而绑定一个接口能固定这个 MAC 地址。自然而然地选择 eth0 作为它的地址,所以能看到 vmbr0 和 eth0 有相同的 MAC 地址。

这么想象:新建一个 vmbr 就相当于在主板上添加了一张 PCIe 交换机,增加了几个只有本机认得到的 Ethernet 网口,内核是直接连接到这张网卡的,中间不再需要什么网线了,这台设备成了个交换机。

Transclude of 2025-08-19.excalidraw#^framenelqgkse7ux3gvrq4rpmd

虚拟机的 tap 设备通过一根虚拟网线接上交换机,br0 之间的虚拟机可以自由连接,但它们需要配置好 IP 地址。
注意此时 br0 和 eth0 是不能通讯的,因为我们没添加连通设置。但是可以 tcpdump

而如果使用 ip link set master 把物理网口也接上交换机,那么物理网口就被占用了,网口之间以隐形的虚拟网线连接了起来,eth0 透明化了,就像是网线转接头,想象网线从 eth0 背后绕出连接到 br0 上。
此时内核会为这个虚拟交换机 br0 创建一个特殊的管理端口,这个端口就是 br0 这个网络设备本身。给交换机设置的 IP 实际是在给这个管理端口设置。

bridge 设备自己有一个 Mac 地址,并可以绑定一个 IP 地址。换句话说 bridge 存在一个隐含的网络接口连接到本机的 Network Protocol Stack。Linux 网络虚拟化技术(三)bridge 虚拟设备 :: Rectcircle Blog

Transclude of 2025-08-19.excalidraw#^frameixehl8ylfy5p0p-zszy89

另一种常见的管理场景是两个物理网口,一个 eth0 直接配置 IP 地址用于连接 Hypervisor,另一个 eth1 用于连接虚拟机。此时 IP 配置在 eth1 而不是 br0 上,因为没有管理的需求,它是透明的二层设备。

Transclude of 2025-08-19.excalidraw#^framea8fznowcpyk-yuhcck3kw

向登录用户展现动态内容

也就是展现 banner / motd,只不过 banner 一般是静态的,motd 是动态的。

Banner 可以通过 sshd_config 的 Banner 参数配置,在登录时展示对应文件的内容,但对 SFTP 也有影响。linux - SSH MOTD per user - Server Fault

目前在 Debain 上,motd 由 motd - Debian Wiki pam_motd 创建,执行 /etc/update-motd.d 下的脚本生成动态内容
动态的 motd 不适合放与当前用户有关的实时信息

cp /etc/update-motd.d/20-fastfetch /etc/profile.d/20-fastfetch.sh
 
# cat /etc/profile.d/20-fastfetch.sh  
#!/usr/bin/env bash  
printf "\n"  
fastfetch --pipe false

比如 fastfetch 应该放在 profile 里,否则读取到的 username / terminal 是错误的,上面是 /etc/update-motd.d 下面是 /etc/profile.d

$ ssh 192.168.9.17
Linux pve 6.14.8-2-pve #1 SMP PREEMPT_DYNAMIC PMX 6.14.8-2 (2025-07-22T10:04Z) x86_64
 
┌───────────────────────────── System Information ─────────────────────────────┐
   Host           root@pve
   OS             Proxmox VE 9.0.5 x86_64
   Uptime         42 mins
   Load Average   0.03, 0.01, 0.00
   Shell          bash 5.2.37
   Terminal       run-parts
└──────────────────────────────────────────────────────────────────────────────┘
                                ● ● ● ● ● ● ● ●
Last login: Tue Aug 19 12:26:56 2025 from 192.168.9.18
 
┌───────────────────────────── System Information ─────────────────────────────┐
   Host           enihsyou@pve
   OS             Proxmox VE 9.0.5 x86_64
   Uptime         42 mins
   Load Average   0.03, 0.01, 0.00
   Shell          bash 5.2.37
   Terminal       /dev/pts/1
└──────────────────────────────────────────────────────────────────────────────┘
                                ● ● ● ● ● ● ● ●
enihsyou@pve:~$

这里的差别还有,Last login 这行出现在上面还是下面

最终参考 linux - Trying to print username at message of the day (MOTD) - Super User 并且把 cat 换成 ps 调用得到了下面的脚本

#!/usr/bin/env bash
# shellcheck disable=SC2086
# Run 'whatever-fetch' on SSH session login
 
# This file is intended to be placed in /etc/update-motd.d/ and
# executed by pam_motd.so upon login (Debian). It may also be placed in /etc/profile.d/
# to run when profile files are loaded; the two differ in execution timing.
 
# read username from 'sshd-session' process
# https://superuser.com/questions/981897/trying-to-print-username-at-message-of-the-day-motd/1178598#1178598
p1=$(ps --no-headers -o ppid $PPID)
p2=$(ps --no-headers -o ppid $p1)
us=$(ps --no-headers -o command $p2 | awk '{ print $2 }' | sed "s/[[:digit:].-]//g")
 
# Separate the following output from the above for nicer appearance
printf "\n"
 
# fastfetch reads the username from the USER environment variable,
# so we override it to change the username displayed in the title format.
# https://github.com/fastfetch-cli/fastfetch/blob/805e2774293d27f0f5b803ff2b35f3332e2a1bb6/src/util/platform/FFPlatform_unix.c#L177
#
# '--pipe false' is required to enable colored output
USER="$us" fastfetch --pipe false

Uploaded to Gist: Run ‘whatever-fetch’ on SSH session login

githubusercontent 图床

直接将图片拖进 GitHub Markdown 编辑框就能触发文件上传,并且获得一个 URL

以前是使用 user-images.githubusercontent.com 域名,最近发现切换到 private-user-images.githubusercontent.com 并且要求 JWT 验证,所以想要当做图床还是有些难度,早些时间就建了一个 enihsyou/StaticStorage: 九条涼果的静态文件存储库。但也不是不行 Deal with private-user-images.githubusercontent.com when Fetching GitHub Discussion Body | by Jun Yu | Medium,不过最好的办法还是走 proxy,gh-proxy.com / jsDelivr.com

似乎从这时候开始支持的 File attachment support in Markdown for gists - GitHub Changelog,并且 github - Do images in ‘user-images.githubusercontent.com’ have expire time? - Stack Overflow 似乎也没有过期时间

自动切换色彩主题的图片

How to make your images in Markdown on GitHub adjust for dark mode and light mode - The GitHub Blog

<picture>
  <source media="(prefers-color-scheme: dark)" srcset="dark-mode-image.png">
  <source media="(prefers-color-scheme: light)" srcset="light-mode-image.png">
  <img alt="Fallback image description" src="default-image.png">
</picture>

网络接口状态 UNKNOWN

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host noprefixroute
       valid_lft forever preferred_lft forever

lo UNKNOWN 是正常的

4: vmbr1: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
    link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff
    inet 192.168.2.1/24 scope global vmbr1
       valid_lft forever preferred_lft forever
    inet6 fe80::306a:f6ff:feed:28a6/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever

在 vmbr 上的 NO-CARRIER DOWN 一般是因为暂时还没有设备(虚拟机)连接到这个虚拟桥上。

5: tailscale0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1280 qdisc fq state UNKNOWN group default qlen 500
    link/none
    inet 100.89.34.112/32 scope global tailscale0
       valid_lft forever preferred_lft forever
    inet6 fd7a:115c:a1e0::a501:2272/128 scope global
       valid_lft forever preferred_lft forever
    inet6 fe80::89c5:afa1:12ca:a987/64 scope link stable-privacy proto kernel_ll
       valid_lft forever preferred_lft forever

Tailscale 是隧道软件,也是正常的