########################################################################
# VoIP Mikrotik script : symetric NAT and firewall filter rules        #
# Author : G. HUSSON, Liberasys, 201904, contact_web _@_ liberasys.com #
# License : EUPL v1.2 - https://opensource.org/licenses/EUPL-1.2       #
# Prerequisits : having preestablished firewall rules and masquerading #
# HOWTO :                                                              #
#   - set variables corresponding to your installation                 #
#   - run the script                                                   #
#   - move the firewall rules in firewall filter rules where it        #
#     shoud be                                                         #
########################################################################

# Set variables (change them !!!!)
:global internalIp "192.168.0.10";
:global internalInterface "bridge-internal";
:global externalIp "1.2.3.4";
:global externalInterface "bridge-internet";
:global voipHttpsPort 443;
:global voipHttpPort 80;
:global voipSIPPort 5060;
:global voipSIPsPort ($voipSIPPort + 1);
:global voipTunnelPort 5090;
:global voipMediaPort "9000-10999";
:global initialNatRulesPosition 0;

# Clean previously defined rules
/ip firewall nat remove [ find comment ~ "(#VOIPscript)" ]
/ip firewall filter remove [ find comment ~ "(#VOIPscript)" ]

# Disable SIP ALG
/ip firewall service-port disable sip

# Set NAT rules (they have to be before any masquerading rule)
/ip firewall nat add action=dst-nat \
    chain=dstnat \
    in-interface=$externalInterface \
    dst-address=$externalIp \
    protocol=tcp \
    dst-port="$voipHttpsPort,$voipHttpPort,$voipSIPPort,$voipSIPsPort,$voipTunnelPort" \
    to-addresses=$internalIp \
    log=yes \
    log-prefix=voip-in \
    place-before=$initialNatRulesPosition \
    comment="VoIP TCP - IN  (#VOIPscript)"

/ip firewall nat add action=src-nat \
    chain=srcnat \
    src-address=$internalIp \
    out-interface=$externalInterface \
    protocol=tcp \
    src-port="$voipHttpsPort,$voipHttpPort,$voipSIPPort,$voipSIPsPort,$voipTunnelPort" \
    to-addresses=$externalIp \
    log=yes \
    log-prefix=voip-out \
    place-before=$initialNatRulesPosition \
    comment="VoIP TCP - OUT, symetric NAT  (#VOIPscript)"

/ip firewall nat add action=dst-nat \
    chain=dstnat \
    in-interface=$externalInterface \
    dst-address=$externalIp \
    protocol=udp \
    dst-port="$voipSIPPort,$voipSIPsPort,$voipTunnelPort,$voipMediaPort" \
    to-addresses=$internalIp \
    log=yes \
    log-prefix=voip-in \
    place-before=$initialNatRulesPosition \
    comment="VoIP UDP - IN  (#VOIPscript)"

/ip firewall nat add action=src-nat \
    chain=srcnat \
    src-address=$internalIp \
    out-interface=$externalInterface \
    protocol=udp \
    src-port="$voipSIPPort,$voipSIPsPort,$voipTunnelPort,$voipMediaPort" \
    to-addresses=$externalIp \
    log=yes \
    log-prefix=voip-out \
    place-before=$initialNatRulesPosition \
    comment="VoIP UDP - OUT, symetric NAT  (#VOIPscript)"

# Set firewall filter rules
/ip firewall filter add action=accept chain=forward \
    in-interface=$externalInterface \
    out-interface=$internalInterface \
    dst-address=$internalIp \
    protocol=tcp \
    dst-port="$voipHttpsPort,$voipHttpPort,$voipSIPPort,$voipSIPsPort,$voipTunnelPort" \
    log=yes \
    log-prefix=voip-in \
    comment="Internet -> VoIP server - TCP  (#VOIPscript)" 

/ip firewall filter add action=accept chain=forward \
    in-interface=$externalInterface \
    out-interface=$internalInterface \
    dst-address=$internalIp \
    protocol=udp \
    dst-port="$voipSIPPort,$voipSIPsPort,$voipTunnelPort,$voipMediaPort" \
    log=yes \
    log-prefix=voip-in \
    comment="Internet -> VoIP server - UDP  (#VOIPscript)" 

# Add drop rule for IP that have been added to "blacklist-sip"
# address list
/ip firewall filter add action=drop \
  chain=forward \
  src-address-list=blacklist-sip \
  comment="blacklist-sip DROP (#VOIPscript)" \

# Add drop rule for IP that have been added to "blacklist-3cxtunnel"
# address list
/ip firewall filter add action=drop \
  chain=forward \
  src-address-list=blacklist-3cxtunnel \
  comment="blacklist-3cxtunnel DROP (#VOIPscript)"

# Add an IP to "sip-blacklist" address list, based on connection
# number (max 10 SIP sessions per IP) and packet rate (max 100 packets
# in 1mn) - adapt it to your field use.
/ip firewall filter add action=add-src-to-address-list \
  chain=forward \
  protocol=udp \
  dst-port=5060 \
  connection-limit=10,32 \
  connection-state=invalid,new,untracked \
  limit=100/1m,0:packet \
  address-list=blacklist-sip \
  address-list-timeout=3h \
  log=yes \
  log-prefix=hacker-sip \
  comment="Add SIP hacker IP to blacklist-sip (#VOIPscript)"

# Add an IP to "blacklist-3cxtunnel" address list, based on connection
# rate (max 4 tunnels per IP) - adapt it to your field use.
/ip firewall filter add action=add-src-to-address-list \
  chain=forward \
  protocol=udp \
  dst-port=5060 \
  connection-limit=4,32 \
  connection-state=invalid,new,untracked \
  address-list=blacklist-3cxtunnel \
  address-list-timeout=3h \
  log=yes \
  log-prefix=hacker-3cxtnl \
  comment="Add SIP hacker IP to iblacklist-3cxtunnel (#VOIPscript)"

# Reference documentation for connection-limit and limit:
#   https://wiki.mikrotik.com/wiki/Manual:IP/Firewall/Filter
# connection-limit: Matches connections per address or address block
#   up to and including given value. Should be used together with
#   connection-state=new and/or with tcp-flags=syn because matcher is
#   very resource intensive.
# limit (integer,time,integer; Default: ): Matches packets up to a
#   limited rate (packet rate or bit rate). Rule using this matcher will
#   match until this limit is reached. Parameters are written in
#   following format: count[/time],burst:mode.
#   * count: packet or bit count per time interval to match
#   * time: specifies the time interval in which the packet or bit count
#       cannot be exceeded (optional, 1s will be used if not specified)
#   * burst: initial number of packets or bits to match: this number
#       gets recharged every 10ms so burst should be at least 1/100
#       of rate per second
#   * mode: packet or bit mode 

# Clean variables
:set internalIp;
:set internalInterface;
:set externalIp;
:set externalInterface;
:set voipHttpsPort;
:set voipHttpPort;
:set voipSIPPort;
:set voipSIPsPort;
:set voipTunnelPort;
:set voipMediaPort;
:set initialNatRulesPosition;