光猫:ZXHN F650(GPON ONU)
硬件环境:openwrt x86
openwrt固件版本:21.02.0 r16279-5cc0535800
由光猫拨号,openwrt直接使用DHCP客户端连接。光猫具体配置如下图:
防火墙讲道理应该是不用设置的。因为看说明好像只是ipv4的设置。
而openwrt的应设置3个接口。分别为与光猫连接的WAN口,这个口只是用于ip4协议使用DHCP4客户端,然后再在这个接口上添加WAN6,协议使用DHCP6客户端用于获取光猫下发的ipv6。而LAN口除了正常的ip4的配置外,还应该添加ipv6 DHCP6服务。如下图
WAN
WAN6
LAN
PC侧的防火墙我就不演示了,实在不行可以直接关掉试试。
openwrt的防火墙可以在网络->防火墙->常规设置。将forward设置为accept。然后再讲区域中的都改成accept。这样就可以连接到你ipv6上的每一个端口。但是这样设置几乎就完全暴露自己了。但是可以先这样试试看是否能连接常用的一些端口。
正常做法是自定义规则来,避免完全暴露。如下
在Firewall - Traffic Rules中添加规则
这里的目标地址可以不填。当然你也可以指定IP,避免lan口上所有的设备都暴露下面的端口。
我这里是直接用的阿里云,因为官方固件对阿里的ddns不是很友好,用阿里云的DDNS API来做。具体做法
1、在openwrt中安装python环境
2、python pip 安装阿里云Python API
3、编写脚本,查询本机IP,如有更改就调用阿里云API更新DNS
4、使用openwrt的定时脚本,定时执行
使用openwrt opkg安装python3,当然也可以用web管理界面安装。其中需要安装 python3、python3-pip、python3-dev、python3-cffi、python3-cff3-src。黑体字这3个会影响到阿里云SDK的正确安装。
按照阿里云的说明,先安装pip install alibabacloud_tea_openapi,然后安装pip install alibabacloud_alidns20150109==2.0.2。到这里阿里云API的环境搭建完成。
脚本可以按照说明书下面的示例写,需要用到AccessKey,这个直接在阿里云的网站上面设置。下面给出一个我自己写的脚本。
Openwrt路由的IP可以通过连接2400:da00:2::29获得,而我个人PC的IP是直接通过ping我PC的计算机名称来获取,我的计算机名为HomeComputer,所以有一行代码是
val = os.popen('ping -6 -c 1 HomeComputer').read()
以下是完整的python刷新openwrt IP的脚本
from socket import AddressFamily
from socket import SocketKind
from socket import socket
from typing import List
import os
import time
import traceback
from alibabacloud_alidns20150109 import client
from alibabacloud_alidns20150109.client import Client as Client
from alibabacloud_alidns20150109 import models as alidns_20150109_models
from alibabacloud_tea_openapi import models as open_api_models
def CalculateComputerIP(ipv6):
newIP = ''
val = os.popen('ping -6 -c 1 HomeComputer').read() #此处使用自己的计算机名称
if 'bad address' in val: #电脑关机休眠的情况
newIP = ipv6
else:
newIP = val[val.rfind('(') + 1:val.rfind(')')]
return newIP
def UpdateRecord():
localtime = time.asctime( time.localtime(time.time()))
print(localtime)
s = socket(AddressFamily.AF_INET6,SocketKind.SOCK_DGRAM)
s.connect(('2400:da00:2::29',80))
ip = s.getsockname()[0]
print('current openwrt ip6->' + ip)
config = open_api_models.Config(
# 您的AccessKey ID,
access_key_id='-----------------',
# 您的AccessKey Secret,
access_key_secret='----------------'
)
config.endpoint='alidns.cn-hangzhou.aliyuncs.com'
client = Client(config)
request = alidns_20150109_models.DescribeDomainRecordsRequest()
request.domain_name = '*******.cn'
request.lang = 'en'
response = client.describe_domain_records(request)
#for record in response.body.domain_records.record:
#print(record.value)
results = [r for r in response.body.domain_records.record if r.type == 'AAAA']
computerResult = results[0]
openwrtResult = results[1]
computerIP = CalculateComputerIP(computerResult.value)
print('current computer ip->' + computerIP)
print('ali computer ip->' + computerResult.value + '\t' + "ali openwrt id->" + openwrtResult.value)
print('ali computer id->' + computerResult.record_id + '\t' + "ali openwrt id->" + openwrtResult.record_id)
isUpdateOpenwrt = False
isUpdateComputer = False
if(openwrtResult.value!=ip):
print('current openwrt ip != ali ip,will be update ddns.')
# 更新AAAA纪录
update_openwrt_request = alidns_20150109_models.UpdateDomainRecordRequest(
rr='openwrt',
type='AAAA',
record_id=openwrtResult.record_id,
value=ip
)
print('update openwrt AAAA record.')
try:
#print('update_openwrt_request:' + update_openwrt_request.__str__)
updateOpenwrtIPReponse = client.update_domain_record(update_openwrt_request)
isUpdateOpenwrt = True
print('update openwrtIP is ok.')
except Exception as e:
print('update openwrtIP record except:' + e.args)
UpdateRecord()
然后使用crontab来设置定时启动上面这个文件就好了。
而PC端的IP可以直接改上面的Python代码来实现,但是经过测试很不稳定,所以我在PC端写了一个服务定时刷新IP进行DDNS。主要的代码如下
public partial class AliDDNSService : ServiceBase
{
Client _ddnsClient;
DescribeDomainRecordsRequest _request;
DescribeDomainRecordsResponse _response;
bool _running = false;
Timer _timer;
public AliDDNSService()
{
InitializeComponent();
_ddnsClient = CreateClient("-------------", "-------------------");
_request = new DescribeDomainRecordsRequest();
_response = new DescribeDomainRecordsResponse();
_timer = new Timer(10 * 60 * 1000);
_timer.Elapsed += RefreshDNS;
}
protected override void OnStart(string[] args)
{
_timer.Start();
}
private void RefreshDNS(object sender, ElapsedEventArgs e)
{
if (_running) return;
try
{
_running = true;
_request.DomainName = "*********.cn";
_request.Lang = "en";
_response = _ddnsClient.DescribeDomainRecords(_request);
var results = _response.Body.DomainRecords.Record.Where(w => w.Type == "AAAA");
var result = results.ElementAt(0); //设计第一个IP为PC的IP
string recordIP = result.Value;
string currentIP = GetCurrenIPV6();
if (recordIP == currentIP) return;
_ddnsClient.UpdateDomainRecord(new UpdateDomainRecordRequest
{
RR = "computer",
Type = "AAAA",
RecordId = result.RecordId,
Value = currentIP
});
}
catch (Exception exp)
{
}
finally
{
_running = false;
}
}
protected override void OnStop()
{
_timer.Stop();
}
protected override void OnPause()
{
_timer.Stop();
}
protected override void OnContinue()
{
_timer.Start();
}
protected override void OnShutdown()
{
_timer.Stop();
}
private Client CreateClient(string accessKeyId, string accessKeySecret)
{
Config config = new Config
{
// 必填,您的 AccessKey ID
AccessKeyId = accessKeyId,
// 必填,您的 AccessKey Secret
AccessKeySecret = accessKeySecret,
};
// 访问的域名
config.Endpoint = "alidns.cn-hangzhou.aliyuncs.com";
return new Client(config);
}
private string GetCurrenIPV6()
{
var hostName = Dns.GetHostName();
var addresses = Dns.GetHostAddresses(hostName);
var list = addresses.Where(w => w.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6);
list = list.Where(l => l.ToString().First() == '2');
return list.OrderBy(l=>l.ToString().Length).First().ToString();
}
}
注意:如果要远程连接PC,需要先使用OpenWrt的Wake On Lan唤醒我们的计算机。
当然也可以写一个脚本完成此项操作,我这里用C#写了一个控制台的程序。思路就是OpenWrt打开SSH,然后通过SSH来操作路由器,让路由器上的WakeOnLan工作。我这个引用了nuget包ssh.net
以下就是我的控制台程序
namespace wakeOnLan
{
internal class Program
{
static SshClient _client;
static void Main(string[] args)
{
try
{
var result = GetOpenwrtIP().Result;
if (!result.Item1)
{
Console.WriteLine("Get openwrt ip failure.");
return;
}
_client = new SshClient($"{result.Item2}", 2222, "root", "root密码");//使用2222作为ssh的端口,这里可以在openwrt中更改
_client.Connect();
var command = _client.RunCommand("/usr/bin/etherwake -D -i br-lan -b mac地址");//这里填入自己的设备MAC地址
Console.WriteLine(command.Result);
_client.Disconnect();
_client.Dispose();
}
catch (Exception exp)
{
Console.WriteLine(exp.Message);
}
finally
{
Console.WriteLine("Wait a minute.");
}
}
static async Task<(bool, string)> GetOpenwrtIP()
{
Process process = new Process();
process.StartInfo.FileName = "cmd.exe";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.CreateNoWindow = true;
process.Start();
process.StandardInput.WriteLine("ping -6 -n 1 *****.*****.cn");//通过域名来找到IPv6
process.StandardInput.Flush();
process.StandardInput.Close();
Thread.Sleep(1000);
string content = await process.StandardOutput.ReadToEndAsync();
if (!content.Contains("0% loss")) return (false, "");
var contents = content.Split("\r\n");
content = contents[5];
content = content.Split("]")[0].Split("[")[1];
return (true, content);
}
}
}
编译后,将可执行的文件拷贝至Windows/system32文件夹下,即可在cmd或powershell中运行。
这里因为是这电脑没有ipv6所以失败了