Asterisk on OpenWRT

Asterisk on OpenWRT

2020春节因为新型肺炎的疫情,在家要呆😳近20天,于是把一直想折腾的VoIP on OpenWRT完成。参考了配置EPON家庭网关接入中国电信电话交换IP网络及其另外一篇博文,但一些设置没法同样实现。

1. 准备工作

1.1 网络拓扑

光纤是电信的,光猫是ZTE的F450G,桥接到X86的OpenWRT(ver:19.07.1)拨号上网,同时设置VLAN接入电信IPTV。F450G的一个LAN口接软路由的WAN(物理eth0)口,F450G的LAN保留192.168.1.1,电信的模拟电话仍然接在F450G的电话口上;软路由的LAN使用192.168.10.x网段,物理eth0上建立一个新的连接DSL,ip地址是192.168.1.5:
网络拓扑

DSL接口设置:
设置
由于需要接在F450G上的模拟电话工作,需要设置防火墙。DSL的Firewall Zone:

1.2 F450G信息收集

拿到超级密码后(网上有方法,不再赘述),开通F450G的telnet,一些电信VoIP的参数需要保存下来:
关于电话的参数很重要,后面需要用到:

可以看到,VoIP的网关为28.81.255.254;DNS为15.192.252.188和15.192.251.188;


路由的信息也很关键,软路由的路由设置需要这些。

还有应用->宽带电话设置->基本配置下的数据,忘记截屏了:
主SIP代理IP地址:sh.ctcims.cn
主SIP外部代理IP地址:BAC10.pd.sh.ctcims.cn
用户号码:+8621xxxxxxxx
鉴权用户名:+8621xxxxxxxx@sh.ctcims.cn
密码:xxxxxx

2. 开始工作

2.1 OpenWRT 相关软件安装

安装dnsmasq-full:

opkg install dnsmasq-full

安装Asterisk相关的软件(chan_PJSIP):

opkg install asterisk16 asterisk16-app-controlplayback asterisk16-app-mp3 asterisk16-bridge-builtin-features asterisk16-bridge-builtin-interval-features asterisk16-bridge-holding asterisk16-bridge-native-rtp asterisk16-bridge-simple asterisk16-bridge-softmix asterisk16-codec-alaw asterisk16-codec-g722 asterisk16-codec-g726 asterisk16-codec-g729 asterisk16-codec-gsm asterisk16-codec-opus asterisk16-codec-ulaw asterisk16-format-gsm asterisk16-format-wav asterisk16-format-wav-gsm asterisk16-pjsip asterisk16-res-adsi asterisk16-res-http-websocket asterisk16-res-pjproject asterisk16-res-rtp-asterisk asterisk16-res-smdi asterisk16-res-sorcery asterisk16-sounds asterisk16-voicemail

2.2 参数配置

在F450G上只能有一个Vlan 46的网络,没法按照开头所参考的blog,但思路是建立从Asterisk到F450G的路由:

dnsmasq.conf:
server=/sh.ctcims.cn/15.192.252.188
server=/sh.ctcims.cn/15.192.251.188
addn-hosts=/etc/dnsmasq.hosts.addn

dnsmasq.hosts.addn:
15.192.60.83 sh.ctcims.cn
#15.192.60.87 sh.ctcims.cn

由于中国电信在VoIP上使用的15段和28段IP并不能通过正常路由来转发,需要在软路由上设置路由强迫VoIP相关的15和28网段走Vlan46, 15.192.60.83及15.192.60.87是BAC10.pd.sh.ctcims.cn地址,可以在F450G上ping得到,但使用下来,只能用一个,不然Asterisk会报错(unable to find authenticate header in challenge)。

/etc/config/network:
config route
    option interface 'DSL'
    option target '15.192.0.0'
    option netmask '255.255.0.0'
    option gateway '192.168.1.1'

    config route
    option interface 'DSL'
    option target '28.81.128.0'
    option netmask '255.255.128.0'
    option gateway '192.168.1.1'

这样软路由上的Asterisk就可以和中国电信的VoIP服务器交流。

/etc/asterisk/pjsip.conf:
[transport-udp]
type=transport
protocol=udp    ;udp,tcp,tls,ws,wss
bind=0.0.0.0
local_net=127.0.0.1
local_net=192.168.1.0/24
local_net=192.168.10.0/24

[mytrunk]
type=registration
transport=transport-udp
outbound_auth=mytrunk_auth
server_uri=sip:sh.ctcims.cn:5060
client_uri=sip:+8621xxxxxxxx@sh.ctcims.cn:5060
retry_interval=60
fatal_retry_interval=30
forbidden_retry_interval=600
max_retries=10000
expiration=3600
line=yes
endpoint=mytrunk
auth_rejection_permanent=no

[mytrunk_auth]
type=auth
auth_type=userpass
password=xxxxxx
username=+8621xxxxxxxx0@sh.ctcims.cn
realm=sh.ctcims.cn

[mytrunk]
type=endpoint
transport=transport-udp
context=from-external
disallow=all
allow=ulaw,alaw,gsm,g726,g722,g729
aors=mytrunk
outbound_auth=mytrunk_auth
from_domain=sh.ctcims.cn
from_user=+8621xxxxxxxx
rewrite_contact=yes
force_rport=yes    ;It's a good idea to read the configuration help for each
rtp_symmetric=yes
direct_media=no
dtmf_mode=auto

[mytrunk]
type=aor
qualify_frequency=60
contact=sip:+8621xxxxxxxx@sh.ctcims.cn:5060

[mytrunk]
type=identify
endpoint=mytrunk
match=15.192.0.0/16

[301]
type=endpoint
transport=transport-udp
context=from-internal
disallow=all
allow=ulaw,g729,alaw
;allow=gsm
auth=301
aors=301
direct_media=no
device_state_busy_at=1
allow_subscribe=yes
sub_min_expiry=30

[301]
type=auth
auth_type=userpass
password=301
username=301

[301]
type=aor
max_contacts=3
contact=sip:301@192.168.10.1:5060

extensions.conf

/etc/asterisk/extensions.conf
[from-external]
exten => s,1,Answer()
same => n,BackGround(enter-ext-of-person)
  same => n, WaitExten(5)
exten => _3XX,1,Answer()
   same => n,Dial(PJSIP/${EXTEN},20,tr)
   same => n,Hangup()
exten => i,1,Playback(invalid)
  same => n,Goto(from-external,s,1)
exten => t,1,Playback(goodbye)
  same => n, Hangup()

[from-internal]
include => outbound-local
include => internal-call

[outbound-local]
ignorepat => 9
exten => 119,1,Dial(PJSIP/119@mytrunk)
exten => 9119,1,Dial(PJSIP/119@mytrunk)
exten => 110,1,Dial(PJSIP/110@mytrunk)
exten => 9110,1,Dial(PJSIP/110@mytrunk)
exten => 120,1,Dial(PJSIP/120@mytrunk)
exten => 9120,1,Dial(PJSIP/120@mytrunk)
exten =>_9.,1,Dial(PJSIP/${EXTEN:1}@mytrunk,20,r)
  same => n,Congestion()
  same => 102,Congestion()

[internal-call]
exten => _3XX,1,Answer()
same => n,Dial(PJSIP/${EXTEN},20,tr)
same => n,Hangup()

/etc/asterisk/indications.conf

/etc/asterisk/indications.conf
[general]
country=cn

现在应该可以在电脑上用软电话拨号了,推荐MicroSIP软件,设置简单,日志读取方便,可以在调试时使用。

###2.3 模拟电话接F450G电话口通话
光猫设置如下:

在光猫上增加192.168.1.0网段到软路由LAN192.168.10.0网段的路由:

ip route add 192.168.10.0/24 via 192.168.1.5

在软路由上打开DSL接口相关的防火墙:

##3. 结束部分
到场这个小型电话交换机应该可以很好工作,但还是有些问题:
###3.1 光猫设置的路由没法保存,网页里的静态路由设置总是出错,不能保存。想了一个办法通过软路由telnet到光猫增加,以下是telnet.pl,添加到cron每5分钟跑一次,万一光猫重启,5分钟后这段路由就加上了:

telnet.pl
#!/usr/bin/perl
use Net::Telnet();

my @table=qw(0.0.0.0 128.0.0.0 192.0.0.0 224.0.0.0 240.0.0.0 248.0.0.0 252.0.0.0 254.0.0.0 255.0.0.0 255.128.0.0 255.192.0.0 255.224.0.0 255.240.0.0 255.248.0.0 255.252.0.0 255.254.0.0 255.255.0.0 255.255.128.0 255.255.192.0 255.255.224.0 255.255.240.0 255.255.248.0 255.255.252.0 255.255.254.0 255.255.255.0 255.255.255.128 255.255.255.192 255.255.255.224 255.255.255.240 255.255.255.248 255.255.255.252 255.255.255.254 255.255.255.255);
my $dest_ip="192.168.10.0";
my $dest_gate="192.168.1.5";
my $has_route=0;
my $t=Net::Telnet->new(Host=>"192.168.1.1", Timeout=>10);
$t->login("root","xxxx");
$t->cmd("linuxshell");
@lines=$t->cmd("route | grep 192.168.10.");
$count=@lines;

if ($count eq 0) {
     add_route($t);
 }
 else
 {
      foreach $c (@lines) {
            print $c;
            my ($dest,$gate,$mask,$flag,$metric,$ref,$use,$iface) = split / +/,$c;
            if (($dest eq "192.168.10.0") and ($gate eq $dest_gate))
              {
                   print "Right route: $dest via $gate mask $mask exist!\n";
                   $has_route=1;
                   last;
              }
            else {
             print "Del route: $dest via $gate mask $mask!\n";
                 del_route($t, $dest, mask2cidr($mask));
              }
    }

    if ($has_route eq 0) {
        add_route($t);
    }
}
$t->close();


sub add_route{
    $_[0]->cmd("ip route add 192.168.10.0/24 via 192.168.1.5");
}

sub del_route{
    $_[0]->cmd("ip route del $_[1]/$_[2]");
}

sub mask2cidr {
   for (my $i=0;$i<=32;$i++) {
   if ($table[i] eq $_[0]) {return $i;}
   }
}

每次重启,光猫的语音绑定接口总是epon0.1,需要手动改动LAN,不然光猫上的模拟电话就没法工作。