| 1 | # Copyright (C) 2009-2010 OpenWrt.org |
| 2 | # Copyright (C) 2009 Malte S. Stretz |
| 3 | |
| 4 | export FW_4_ERROR=0 |
| 5 | export FW_6_ERROR=0 |
| 6 | export FW_i_ERROR=0 |
| 7 | export FW_e_ERROR=0 |
| 8 | export FW_a_ERROR=0 |
| 9 | |
| 10 | #TODO: remove this |
| 11 | [ "${-#*x}" == "$-" ] && { |
| 12 | fw() { |
| 13 | fw__exec "$@" |
| 14 | } |
| 15 | } || { |
| 16 | fw() { |
| 17 | local os=$- |
| 18 | set +x |
| 19 | fw__exec "$@" |
| 20 | local rc=$? |
| 21 | set -$os |
| 22 | return $rc |
| 23 | } |
| 24 | } |
| 25 | |
| 26 | fw__exec() { # <action> <family> <table> <chain> <target> <position> { <rules> } |
| 27 | local cmd fam tab chn tgt pos |
| 28 | local i |
| 29 | for i in cmd fam tab chn tgt pos; do |
| 30 | if [ "$1" -a "$1" != '{' ]; then |
| 31 | eval "$i='$1'" |
| 32 | shift |
| 33 | else |
| 34 | eval "$i=-" |
| 35 | fi |
| 36 | done |
| 37 | |
| 38 | fw__rc() { |
| 39 | export FW_${fam#G}_ERROR=$1 |
| 40 | return $1 |
| 41 | } |
| 42 | |
| 43 | fw__dualip() { |
| 44 | fw $cmd 4 $tab $chn $tgt $pos "$@" |
| 45 | fw $cmd 6 $tab $chn $tgt $pos "$@" |
| 46 | fw__rc $((FW_4_ERROR | FW_6_ERROR)) |
| 47 | } |
| 48 | |
| 49 | fw__autoip() { |
| 50 | local ip4 ip6 |
| 51 | shift |
| 52 | while [ "$1" != '}' ]; do |
| 53 | case "$1" in |
| 54 | *:*) ip6=1 ;; |
| 55 | *.*.*.*) ip4=1 ;; |
| 56 | esac |
| 57 | shift |
| 58 | done |
| 59 | shift |
| 60 | if [ "${ip4:-4}" == "${ip6:-6}" ]; then |
| 61 | echo "fw: can't mix ip4 and ip6" >&2 |
| 62 | return 1 |
| 63 | fi |
| 64 | local ver=${ip4:+4}${ip6:+6} |
| 65 | fam=i |
| 66 | fw $cmd ${ver:-i} $tab $chn $tgt $pos "$@" |
| 67 | fw__rc $? |
| 68 | } |
| 69 | |
| 70 | fw__has() { |
| 71 | local tab=${1:-$tab} |
| 72 | if [ $tab == '-' ]; then |
| 73 | type $app > /dev/null 2> /dev/null |
| 74 | fw__rc $(($? & 1)) |
| 75 | return |
| 76 | fi |
| 77 | local mod |
| 78 | eval "mod=\$FW_${fam#G}_${tab}" |
| 79 | if [ "$mod" ]; then |
| 80 | fw__rc $mod |
| 81 | return |
| 82 | fi |
| 83 | case "$fam" in |
| 84 | *4) mod=iptable_${tab} ;; |
| 85 | *6) mod=ip6table_${tab} ;; |
| 86 | *) mod=. ;; |
| 87 | esac |
| 88 | grep -q "^${mod} " /proc/modules |
| 89 | mod=$? |
| 90 | export FW_${fam}_${tab}=$mod |
| 91 | fw__rc $mod |
| 92 | } |
| 93 | |
| 94 | fw__err() { |
| 95 | local err |
| 96 | eval "err=\$FW_${fam}_ERROR" |
| 97 | fw__rc $err |
| 98 | } |
| 99 | |
| 100 | local app= |
| 101 | local pol= |
| 102 | case "$fam" in |
| 103 | *4) [ $FW_DISABLE_IPV4 == 0 ] && app=iptables || return ;; |
| 104 | *6) [ $FW_DISABLE_IPV6 == 0 ] && app=ip6tables || return ;; |
| 105 | i) fw__dualip "$@"; return ;; |
| 106 | I) fw__autoip "$@"; return ;; |
| 107 | e) app=ebtables ;; |
| 108 | a) app=arptables ;; |
| 109 | -) fw $cmd i $tab $chn $tgt $pos "$@"; return ;; |
| 110 | *) return 254 ;; |
| 111 | esac |
| 112 | case "$tab" in |
| 113 | f) tab=filter ;; |
| 114 | m) tab=mangle ;; |
| 115 | n) tab=nat ;; |
| 116 | r) tab=raw ;; |
| 117 | -) tab=filter ;; |
| 118 | esac |
| 119 | case "$cmd:$chn:$tgt:$pos" in |
| 120 | add:*:-:*) cmd=new-chain ;; |
| 121 | add:*:*:-) cmd=append ;; |
| 122 | add:*:*:$) cmd=append ;; |
| 123 | add:*:*:*) cmd=insert ;; |
| 124 | del:-:*:*) cmd=delete-chain; fw flush $fam $tab ;; |
| 125 | del:*:-:*) cmd=delete-chain; fw flush $fam $tab $chn ;; |
| 126 | del:*:*:*) cmd=delete ;; |
| 127 | flush:*) ;; |
| 128 | policy:*) pol=$tgt; tgt=- ;; |
| 129 | has:*) fw__has; return ;; |
| 130 | err:*) fw__err; return ;; |
| 131 | list:*) cmd="numeric --verbose --$cmd" ;; |
| 132 | *) return 254 ;; |
| 133 | esac |
| 134 | case "$chn" in |
| 135 | -) chn= ;; |
| 136 | esac |
| 137 | case "$tgt" in |
| 138 | -) tgt= ;; |
| 139 | esac |
| 140 | |
| 141 | local rule_offset |
| 142 | case "$pos" in |
| 143 | ^) pos=1 ;; |
| 144 | $) pos= ;; |
| 145 | -) pos= ;; |
| 146 | +) eval "rule_offset=\${FW__RULE_OFS_${app}_${tab}_${chn}:-1}" ;; |
| 147 | esac |
| 148 | |
| 149 | if ! fw__has - family || ! fw__has $tab ; then |
| 150 | export FW_${fam}_ERROR=0 |
| 151 | return 0 |
| 152 | fi |
| 153 | |
| 154 | case "$fam" in |
| 155 | G*) shift; while [ $# -gt 0 ] && [ "$1" != "{" ]; do shift; done ;; |
| 156 | esac |
| 157 | |
| 158 | if [ $# -gt 0 ]; then |
| 159 | shift |
| 160 | if [ $cmd == delete ]; then |
| 161 | pos= |
| 162 | fi |
| 163 | fi |
| 164 | |
| 165 | local cmdline="$app --table ${tab} --${cmd} ${chn} ${pol} ${rule_offset:-${pos}} ${tgt:+--jump "$tgt"}" |
| 166 | while [ $# -gt 1 ]; do |
| 167 | # special parameter handling |
| 168 | case "$1:$2" in |
| 169 | -p:icmp*|-p:1|-p:58|--protocol:icmp*|--protocol:1|--protocol:58) |
| 170 | [ "$app" = ip6tables ] && \ |
| 171 | cmdline="$cmdline -p icmpv6" || \ |
| 172 | cmdline="$cmdline -p icmp" |
| 173 | shift |
| 174 | ;; |
| 175 | --icmp-type:*|--icmpv6-type:*) |
| 176 | local icmp_type |
| 177 | if [ "$app" = ip6tables ] && fw_check_icmptype6 icmp_type "$2"; then |
| 178 | cmdline="$cmdline $icmp_type" |
| 179 | elif [ "$app" = iptables ] && fw_check_icmptype4 icmp_type "$2"; then |
| 180 | cmdline="$cmdline $icmp_type" |
| 181 | else |
| 182 | local fam=IPv4; [ "$app" = ip6tables ] && fam=IPv6 |
| 183 | fw_log info "ICMP type '$2' is not valid for $fam address family, skipping rule" |
| 184 | return 1 |
| 185 | fi |
| 186 | shift |
| 187 | ;; |
| 188 | *) cmdline="$cmdline $1" ;; |
| 189 | esac |
| 190 | shift |
| 191 | done |
| 192 | |
| 193 | [ -n "$FW_TRACE" ] && echo $cmdline >&2 |
| 194 | |
| 195 | $cmdline |
| 196 | |
| 197 | local rv=$? |
| 198 | [ $rv -eq 0 ] && [ -n "$rule_offset" ] && \ |
| 199 | export -- "FW__RULE_OFS_${app}_${tab}_${chn}=$(($rule_offset + 1))" |
| 200 | fw__rc $rv |
| 201 | } |
| 202 | |
| 203 | fw_get_port_range() { |
| 204 | local _var=$1 |
| 205 | local _ports=$2 |
| 206 | local _delim=${3:-:} |
| 207 | if [ "$4" ]; then |
| 208 | fw_get_port_range $_var "${_ports}-${4}" $_delim |
| 209 | return |
| 210 | fi |
| 211 | |
| 212 | local _first=${_ports%-*} |
| 213 | local _last=${_ports#*-} |
| 214 | if [ "${_first#!}" != "${_last#!}" ]; then |
| 215 | export -- "$_var=$_first$_delim${_last#!}" |
| 216 | else |
| 217 | export -- "$_var=$_first" |
| 218 | fi |
| 219 | } |
| 220 | |
| 221 | fw_get_family_mode() { |
| 222 | local _var="$1" |
| 223 | local _hint="$2" |
| 224 | local _zone="$3" |
| 225 | local _mode="$4" |
| 226 | |
| 227 | local _ipv4 _ipv6 |
| 228 | [ -n "$FW_ZONES4$FW_ZONES6" ] && { |
| 229 | list_contains FW_ZONES4 $_zone && _ipv4=1 || _ipv4=0 |
| 230 | list_contains FW_ZONES6 $_zone && _ipv6=1 || _ipv6=0 |
| 231 | } || { |
| 232 | _ipv4=$(uci_get_state firewall core ${_zone}_ipv4 0) |
| 233 | _ipv6=$(uci_get_state firewall core ${_zone}_ipv6 0) |
| 234 | } |
| 235 | |
| 236 | case "$_hint:$_ipv4:$_ipv6" in |
| 237 | *4:1:*|*:1:0) export -n -- "$_var=G4" ;; |
| 238 | *6:*:1|*:0:1) export -n -- "$_var=G6" ;; |
| 239 | *) export -n -- "$_var=$_mode" ;; |
| 240 | esac |
| 241 | } |
| 242 | |
| 243 | fw_get_negation() { |
| 244 | local _var="$1" |
| 245 | local _flag="$2" |
| 246 | local _value="$3" |
| 247 | |
| 248 | [ "${_value#!}" != "$_value" ] && \ |
| 249 | export -n -- "$_var=! $_flag ${_value#!}" || \ |
| 250 | export -n -- "$_var=${_value:+$_flag $_value}" |
| 251 | } |
| 252 | |
| 253 | fw_get_subnet4() { |
| 254 | local _var="$1" |
| 255 | local _flag="$2" |
| 256 | local _name="$3" |
| 257 | |
| 258 | local _ipaddr="$(uci_get_state network "${_name#!}" ipaddr)" |
| 259 | local _netmask="$(uci_get_state network "${_name#!}" netmask)" |
| 260 | |
| 261 | case "$_ipaddr" in |
| 262 | *.*.*.*) |
| 263 | [ "${_name#!}" != "$_name" ] && \ |
| 264 | export -n -- "$_var=! $_flag $_ipaddr/${_netmask:-255.255.255.255}" || \ |
| 265 | export -n -- "$_var=$_flag $_ipaddr/${_netmask:-255.255.255.255}" |
| 266 | ;; |
| 267 | *) export -n -- "$_var=" ;; |
| 268 | esac |
| 269 | } |
| 270 | |
| 271 | fw_check_icmptype4() { |
| 272 | local _var="$1" |
| 273 | local _type="$2" |
| 274 | case "$_type" in |
| 275 | ![0-9]*) export -n -- "$_var=! --icmp-type ${_type#!}"; return 0 ;; |
| 276 | [0-9]*) export -n -- "$_var=--icmp-type $_type"; return 0 ;; |
| 277 | esac |
| 278 | |
| 279 | [ -z "$FW_ICMP4_TYPES" ] && \ |
| 280 | export FW_ICMP4_TYPES=$( |
| 281 | iptables -p icmp -h 2>/dev/null | \ |
| 282 | sed -n -e '/^Valid ICMP Types:/ { |
| 283 | n; :r; |
| 284 | /router-advertisement/d; |
| 285 | /router-solicitation/d; |
| 286 | s/[()]/ /g; s/[[:space:]]\+/\n/g; p; n; b r |
| 287 | }' | sort -u |
| 288 | ) |
| 289 | |
| 290 | local _check |
| 291 | for _check in $FW_ICMP4_TYPES; do |
| 292 | if [ "$_check" = "${_type#!}" ]; then |
| 293 | [ "${_type#!}" != "$_type" ] && \ |
| 294 | export -n -- "$_var=! --icmp-type ${_type#!}" || \ |
| 295 | export -n -- "$_var=--icmp-type $_type" |
| 296 | return 0 |
| 297 | fi |
| 298 | done |
| 299 | |
| 300 | export -n -- "$_var=" |
| 301 | return 1 |
| 302 | } |
| 303 | |
| 304 | fw_check_icmptype6() { |
| 305 | local _var="$1" |
| 306 | local _type="$2" |
| 307 | case "$_type" in |
| 308 | ![0-9]*) export -n -- "$_var=! --icmpv6-type ${_type#!}"; return 0 ;; |
| 309 | [0-9]*) export -n -- "$_var=--icmpv6-type $_type"; return 0 ;; |
| 310 | esac |
| 311 | |
| 312 | [ -z "$FW_ICMP6_TYPES" ] && \ |
| 313 | export FW_ICMP6_TYPES=$( |
| 314 | ip6tables -p icmpv6 -h 2>/dev/null | \ |
| 315 | sed -n -e '/^Valid ICMPv6 Types:/ { |
| 316 | n; :r; s/[()]/ /g; s/[[:space:]]\+/\n/g; p; n; b r |
| 317 | }' | sort -u |
| 318 | ) |
| 319 | |
| 320 | local _check |
| 321 | for _check in $FW_ICMP6_TYPES; do |
| 322 | if [ "$_check" = "${_type#!}" ]; then |
| 323 | [ "${_type#!}" != "$_type" ] && \ |
| 324 | export -n -- "$_var=! --icmpv6-type ${_type#!}" || \ |
| 325 | export -n -- "$_var=--icmpv6-type $_type" |
| 326 | return 0 |
| 327 | fi |
| 328 | done |
| 329 | |
| 330 | export -n -- "$_var=" |
| 331 | return 1 |
| 332 | } |
| 333 | |