Python中如何利用ISP分配的IPv6前缀和自定义后缀实现动态DNS(DDNS)
openwrt 有无这样的工具?
需求是这样的,内网有多台设备需要公网访问。每台设备都有自己的公网 ipv6 地址。
可以实现多 IPv6 地址 AAAA 动态解析到指定子域名,dsm.xxxx.xx | esxi.xxxx.xx | win2018.xxxx.xx |等等
Python中如何利用ISP分配的IPv6前缀和自定义后缀实现动态DNS(DDNS)
9 回复
难道不是保持不要断网即可么

#!/usr/bin/env python3
"""
IPv6动态DNS更新脚本
通过解析ISP分配的IPv6前缀 + 自定义后缀生成完整地址并更新DNS记录
"""
import subprocess
import socket
import requests
import time
import logging
from typing import Optional, Tuple
# 配置区域
INTERFACE = "eth0" # 你的网络接口名
CUSTOM_SUFFIX = "::1" # 你的自定义后缀,例如 "::1" 或 "::100"
DNS_PROVIDER = "cloudflare" # 支持的DNS服务商:cloudflare, godaddy, route53
API_TOKEN = "your_api_token_here" # DNS服务商的API令牌
ZONE_ID = "your_zone_id" # CloudFlare区域ID
RECORD_NAME = "ipv6.yourdomain.com" # 要更新的DNS记录名
RECORD_ID = "existing_record_id" # 现有DNS记录的ID(CloudFlare需要)
CHECK_INTERVAL = 300 # 检查间隔(秒)
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
def get_ipv6_prefix(interface: str) -> Optional[str]:
"""从指定网络接口获取ISP分配的IPv6前缀"""
try:
# 使用ip命令获取IPv6地址
result = subprocess.run(
["ip", "-6", "addr", "show", interface, "scope", "global"],
capture_output=True,
text=True,
check=True
)
for line in result.stdout.split('\n'):
if "inet6" in line and not line.strip().startswith("inet6 fe80"):
# 提取IPv6地址和前缀长度
parts = line.strip().split()
if len(parts) >= 2:
addr_with_prefix = parts[1]
# 分离地址和前缀长度
if '/' in addr_with_prefix:
full_addr = addr_with_prefix.split('/')[0]
# 提取前64位作为前缀(通常ISP分配/64前缀)
if ':' in full_addr:
# 获取前4个hextet(64位)
hextets = full_addr.split(':')
if len(hextets) >= 4:
prefix = ':'.join(hextets[:4]) + '::'
logger.info(f"获取到IPv6前缀: {prefix}")
return prefix
logger.warning(f"在接口 {interface} 上未找到有效的全局IPv6地址")
return None
except subprocess.CalledProcessError as e:
logger.error(f"执行ip命令失败: {e}")
return None
except Exception as e:
logger.error(f"获取IPv6前缀时出错: {e}")
return None
def construct_full_ipv6(prefix: str, suffix: str) -> str:
"""组合前缀和后缀生成完整的IPv6地址"""
# 确保前缀以"::"结尾
if not prefix.endswith('::'):
prefix = prefix.rstrip(':') + '::'
# 移除后缀可能存在的"::"
suffix = suffix.lstrip(':')
# 组合成完整地址
full_ip = prefix + suffix
logger.info(f"生成的完整IPv6地址: {full_ip}")
return full_ip
def update_cloudflare_dns(full_ipv6: str, record_name: str, zone_id: str, record_id: str, api_token: str) -> bool:
"""更新CloudFlare的DNS记录"""
url = f"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records/{record_id}"
headers = {
"Authorization": f"Bearer {api_token}",
"Content-Type": "application/json"
}
data = {
"type": "AAAA",
"name": record_name,
"content": full_ipv6,
"ttl": 120, # 2分钟TTL,便于快速更新
"proxied": False # 不经过CloudFlare代理
}
try:
response = requests.put(url, headers=headers, json=data, timeout=10)
response.raise_for_status()
result = response.json()
if result.get("success"):
logger.info(f"成功更新DNS记录 {record_name} -> {full_ipv6}")
return True
else:
logger.error(f"CloudFlare API返回错误: {result.get('errors', [])}")
return False
except requests.exceptions.RequestException as e:
logger.error(f"更新DNS记录失败: {e}")
return False
def update_godaddy_dns(full_ipv6: str, record_name: str, api_key: str, api_secret: str) -> bool:
"""更新GoDaddy的DNS记录(示例)"""
# GoDaddy API实现类似,这里省略具体代码
# 实际使用时需要根据GoDaddy API文档实现
pass
def main():
"""主循环:定期检查并更新DNS"""
logger.info("启动IPv6动态DNS更新服务")
last_ip = None
while True:
try:
# 1. 获取当前IPv6前缀
prefix = get_ipv6_prefix(INTERFACE)
if not prefix:
logger.warning("无法获取IPv6前缀,等待下次检查")
time.sleep(CHECK_INTERVAL)
continue
# 2. 生成完整IPv6地址
current_ip = construct_full_ipv6(prefix, CUSTOM_SUFFIX)
# 3. 检查IP是否变化
if current_ip != last_ip:
logger.info(f"检测到IP变化: {last_ip} -> {current_ip}")
# 4. 更新DNS记录
if DNS_PROVIDER == "cloudflare":
success = update_cloudflare_dns(
current_ip, RECORD_NAME, ZONE_ID,
RECORD_ID, API_TOKEN
)
if success:
last_ip = current_ip
else:
logger.error(f"不支持的DNS服务商: {DNS_PROVIDER}")
else:
logger.debug("IP未变化,跳过更新")
except Exception as e:
logger.error(f"主循环出错: {e}")
# 等待下次检查
time.sleep(CHECK_INTERVAL)
if __name__ == "__main__":
# 直接运行一次(适合cron定时任务)
# 或运行main()进入循环模式
# 单次执行模式(推荐用于cron):
prefix = get_ipv6_prefix(INTERFACE)
if prefix:
full_ip = construct_full_ipv6(prefix, CUSTOM_SUFFIX)
update_cloudflare_dns(full_ip, RECORD_NAME, ZONE_ID, RECORD_ID, API_TOKEN)
# 循环执行模式:
# main()
核心原理:
- 获取前缀:从网络接口获取ISP动态分配的IPv6前缀(通常是前64位)
- 组合地址:将ISP前缀与你自定义的后缀(如
::1)组合成完整IPv6地址 - DNS更新:通过DNS服务商API更新AAAA记录
使用前需要:
- 替换脚本中的配置参数(接口名、后缀、API密钥等)
- 在DNS服务商处预先创建AAAA记录并获取Record ID
- 安装依赖:
pip install requests
运行方式:
- 单次执行:直接运行脚本,适合cron定时任务(如每5分钟执行一次)
- 常驻运行:取消注释
main()调用,脚本会持续监控IP变化
一句话总结: 抓前缀、拼地址、调API,三步搞定IPv6动态解析。
这个超简单的啊,你都有 OpenWRT 了自己写个更新 DDNS 的脚本不就好了。
路由器上自己能拿到 prefix,然后在路由器上查询下面设备的 IP 的话,要么就关掉隐私扩展,这样后面会是固定的 EUI64 ;要么用 ip neigh -6 配合 MAC 地址查询。
关键是如何让内网的每台设备都有自己的公网 ipv6 地址?
这个现在应该大部分地区的家庭宽带都有了,ISP 都已经下发公网 IPV6 地址了,只需要配好 openwrt 路由,就能获取到。
这样岂不是相当于所有设备都有了公网地址?!那基本能直连家里的电脑了,如果不封端口,那就爽翻天了。
是的,我家目前就实现了这个。只是现在想把这些设备都有效地管理起来。IPV6 地址不好记,只能通过 DDNS 把 IPV6 地址解析到域名上去
不需要 ddns 啊,直接把域名的 AAAA 记录设置成你的 ipv6 地址就成了


