Linux 的 PPPoE 拨号连接以及 ip-up 脚本(Debian 版)

虽然 PPPoE - Debian Wiki 给的还是用 pppoeconf 这个包来配置,但是其实它最近一次功能性更新是在 2014 年(Commits · master · Debian / pppoeconf · GitLab),不过也还能用。

开始动手前,可以去 ppp - ArchWiki 看一看,arch 的 wiki 确实详细,虽然他里面提到的 pppoeconfig 和 开机自启的方法在 Debian 上时用不了的,但是可以了解一下 ppp 包的配置文件分别代表什么。

使用 pppoeconf 配置

如果彻底没网装不了 pppoeconf,可以按照上面给的 arch wiki 手动拨号(也要同时参考本文下方的“具体更改”节)。

sudo apt install pppoeconf,之后直接输入 pppoeconf 然后回车,除了要输入的全部保持默认选择,输入 username 时要把他给的 username 这个占位符删了。(如果有多个网卡,在第一步会让选择)

启动 pon dsl-provider

停止 poff

Warning

由于未知原因,请不要使用 NetworkManager 的 nmcli 或者 nmtui 管理你的网络——这样 outbound 没问题,但是 inbound 会连不上。

具体发生了什么?

首先,它在 /etc/ppp/peers 中新建了一个 dsl-provider 文件,这个文件夹中每个文件都是一个连接配置,文件名是连接名称。自动生成的应该如下

# Minimalistic default options file for DSL/PPPoE connections

noipdefault
defaultroute
replacedefaultroute
hide-password
#lcp-echo-interval 30
#lcp-echo-failure 4
noauth
persist
#mtu 1492
#persist
#maxfail 0
#holdoff 20
plugin rp-pppoe.so
nic-enp1s0
user "your_username"
usepeerdns

密码保存在 /etc/ppp/pap-secrets/etc/ppp/chap-secrets 文件中。

通过 ppp 获取的 nameserver 会存储在 /etc/ppp/resolv.conf 中。

接着看 /etc/network/interfaces,多出了以下内容:

auto dsl-provider
iface dsl-provider inet ppp
pre-up /bin/ip link set enp1s0 up # line maintained by pppoeconf
provider dsl-provider

auto enp1s0
iface enp1s0 inet manual

按照 Ubuntu Manpage: /etc/network/interfaces 说法,上面四行等价于 pon dsl-provider,并且将它与 enp1s0 网卡关联,下面就是 auto 设置 enp1s0 网卡了。这样相当于可以开机启动 PPPoE 连接。

如果开机 ifconfig 没看到 ppp0 网卡,稍等片刻或者 journalctl -b --grep=pppd 看看——拨号也是需要时间的。

固定 interface 名称

如果你的 dsl 服务商不稳定,需要经常重启 ppp 服务,那么这段内容可能对你有用。一般来说,poff 之后再 pon 的话,网口名 ppp0 会变成 ppp1,依次递增,如果你有脚本依赖于 ppp0 或者对 ppp0 做了 ddns,或许想要固定这一名称。

参考:

https://unix.stackexchange.com/questions/145692/naming-ppp-interfaces

简单来讲,就是在 /etc/ppp/peers/dsl-provider 中新增一行 ifname 名字 即可(可能不是对所有发行版生效,至少本文探讨的 Debian 和大多数应该都可以)。

因此,最终我的配置是这样的, ifname 选用的是 pppdsl

# Minimalistic default options file for DSL/PPPoE connections

noipdefault
defaultroute
replacedefaultroute
hide-password
#lcp-echo-interval 30
#lcp-echo-failure 4
noauth
persist
#mtu 1492
#persist
#maxfail 0
#holdoff 20
plugin rp-pppoe.so
nic-enp1s0
user "thePlaceholderUsername"
usepeerdns
ifname pppdsl

ip-up 的设置

在 Debian 上(Debian12.2)一个卡住了我很久的事情就是在 /etc/ppp 中给了 ip-up.d 等文件夹,但实际上并没有人去调用这些文件夹中的脚本!pppd 调用的时 ip-up 等脚本,需要 ip-up 自己去 run-parts 那些.d 文件夹中的脚本。我不清楚其他发行版时如何的,但是如果你发现自己的 /etc/ppp/ 下没有 ip-up 等脚本,请自己新建一个,并且一定要有 #! 开头。

比如可以在 ip-up 将 ppp 获得的 nameserver 写入到系统中(需要配置文件中包含 userpeerdns):

#!/bin/sh

if [ "$USEPEERDNS" = "1" -a -f /etc/ppp/resolv.conf ]; then
  [ -e /etc/resolv.conf ] && mv /etc/resolv.conf /etc/resolv.conf.backup.${IFNAME}
  mv /etc/ppp/resolv.conf /etc/resolv.conf
  chmod 644 /etc/resolv.conf
fi

copied from https://github.com/mdlawson/hoth-server/blob/master/etc/ppp/ip-up.d/00-dns.sh

由于这是一台无公网 IP 的虚拟机,它拨号上的网是我唯一从网络上访问它的办法,因此,我需要一些办法获取它每次重新拨号之后获得的 IPv4 和 IPv6 地址。一个办法就是使用 ip-up 脚本,另一个则是用 DDNS。本文着重介绍一下 ip-up 脚本的办法,在拨号成功后,用 ip 命令获取地址,然后通过 Bark 推送给自己。

获取 IP

LLM 帮我写了这两条:

ip -4 addr show dev pppdsl | grep inet | awk '{print $2}' | cut -d/ -f1 # 获取IPv4 地址
ip -6 addr show dev pppdsl | grep inet6 | grep -v ' scope link ' | awk '{print $2}' | cut -d/ -f1 # 获取 IPv6 地址,并且不会返回本地地址

其中 pppdsl 是前面 固定 interface 名称 中使用的 ifname

Tip

如果你选择的安装镜像过于干净而没有 ip 命令,请安装 iproute2 包。

编写通知脚本

下面是我用的 /etc/ppp/ip-up/etc/ppp/ipv6-up 内容。

首先它会用上面提到的命令获取 IP,然后发起一个请求给 Bark 的服务器来给我的手机推送通知。

需要注意的点:

  • 会同时向 stdout 和在文件中设定的 LOG_FILE 变量的文件中输出日志。
  • 如果没获取到或者那里出了问题也会有相应通知。
  • 由于可能存在的特殊字符,需要做 URL Encode。urlencode() 函数就是干这事的。
  • 似乎是因为获取到 IPv4 地址而还没来得及获取 IPv6 时,系统已经分配了一个本地 IPv6 地址导致 ipv6-up 被调用,所以我在 ipv6-up 中等待了 10 秒才开始获取地址。
#!/bin/bash
# /etc/ppp/ip-up
# https://yfi.moe/post/debian-pppoe

/etc/ppp/ip-up.d/00-dns

urlencode() {
  local string="${1}"
  local strlen=${#string}
  local encoded=""
  local pos c o

  for (( pos=0 ; pos<strlen ; pos++ )); do
     c=${string:$pos:1}
     case "$c" in
        [-_.~a-zA-Z0-9] ) o="${c}" ;;
        * )               printf -v o '%%%02x' "'$c"
     esac
     encoded+="${o}"
  done
  echo "${encoded}"    # You can either set a return variable (FASTER)
  REPLY="${encoded}"   #+or echo the result (EASIER)... or both... :p
}

LOG_FILE="/root/ppp-up4.log"

log() {
  local message="$1"  # 要打印的消息

  # 打印到stdout
  echo -e "$message"

  # 写入日志文件
  echo -e "$message" >> "$LOG_FILE"
}

current_time=$(date "+%Y-%m-%d %H:%M:%S")

log "============= IPv4 @ $current_time ==============="
IPADDR=`ip -4 addr show dev pppdsl | grep inet | awk '{print $2}' | cut -d/ -f1`

if [ -n "$IPADDR" ]; then
  log "IPv4: $IPADDR"
  IPADDR_ENC=$(urlencode "$IPADDR")
  URL="https://api.day.app/doUX9Cx9QBnYbD9hPKEX9J/ANA-PPP-IPv4/$IPADDR_ENC"
  curl -s "$URL" | tee -a $LOG_FILE
  log ""
else
  log "NO IPv4 addr found"
fi

log "********************************************************\n"
#!/bin/bash
# /etc/ppp/ipv6-up
# https://yfi.moe/post/debian-pppoe

LOG_FILE="/root/ppp-up6.log"

urlencode() {
  local string="${1}"
  local strlen=${#string}
  local encoded=""
  local pos c o

  for (( pos=0 ; pos<strlen ; pos++ )); do
     c=${string:$pos:1}
     case "$c" in
        [-_.~a-zA-Z0-9] ) o="${c}" ;;
        * )               printf -v o '%%%02x' "'$c"
     esac
     encoded+="${o}"
  done
  echo "${encoded}"    # You can either set a return variable (FASTER) 
  REPLY="${encoded}"   #+or echo the result (EASIER)... or both... :p
}

log() {
  local message="$1"  # 要打印的消息

  # 打印到stdout
  echo -e "$message"

  # 写入日志文件
  echo -e "$message" >> "$LOG_FILE"
}

current_time=$(date "+%Y-%m-%d %H:%M:%S")

sleep 10 # 一开始只能拿到内网ipv6,应该是外网的ipv6还没下发下来,所以先等10s
log "============= IPv6 @ $current_time ==============="

IPADDR=`ip -6 addr show dev pppdsl | grep inet6 | grep -v ' scope link ' | awk '{print $2}' | cut -d/ -f1`

if [ -n "$IPADDR" ]; then
  log "IPv6: $IPADDR"
  IPADDR_ENC=$(urlencode "$IPADDR")
  URL="https://api.day.app/doUX9Cx9QBnYbD9hPKEX9J/ANA-PPP-IPv6/$IPADDR_ENC"
  curl -s "$URL" | tee -a $LOG_FILE
  log ""
else
  log "NO IPv6 addr found"
  ip a >> $LOG_FILE
  URL="https://api.day.app/THEKEYOFYOURDEVICE/ANA-PPP-IPv6/NO-IPv6-addr-found"
  curl -s "$URL"
fi

log "********************************************************\n"

别忘了 chmod 添加执行权限哦!

可以直接执行脚本看看输出,也可以 pon / poff 一下试试。

本文使用“署名-非商业性使用-相同方式共享 4.0 国际(CC BY-NC-SA 4.0)”进行许可。

商业转载请联系站长获得授权,非商业转载请注明本文出处及文章链接。 如果您再混合、转换或者基于本作品进行创作,您必须基于相同的协议分发您贡献的作品。

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.3.0