/
get_ip
executable file
·207 lines (185 loc) · 6.08 KB
/
get_ip
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#!/bin/sh
[ -r /mod/etc/conf/mod.cfg ] && . /mod/etc/conf/mod.cfg
# Global result
IP=""
helpmsg() {
cat << EOF
get_ip - determine external IP address
Usage: $0 [option]
-h, --help - print this help message
IPv4 options:
-a, --all - use all methods (order: stun, route, dsld, webcm) [recommended]
-c, --ctlm - use ctlmgr_ctl
-d, --dsld - use showdsldstat (since firmware 04.86)
-e, --env - display environment variable IPADDR, used by multid
-r, --route - use routing table
-s, --stun - use STUN/VoIP (server: $MOD_GET_IP_STUN)
-w, --webcm - use webcm CGI handler / ctlmgr_ctl
IPv6 options:
-c6, --ctlm6 - use ctlmgr_ctl (IPv6 address)
-d6, --dsld6 - use showdsldstat (IPv6 address)
-cp6, --ctlmpre6 - use ctlmgr_ctl (IPv6 prefix)
-dp6, --dlsdpre6 - use showdsldstat (IPv6 prefix)
Current default IPv4 method: $MOD_GET_IP_METHOD
EOF
}
# Detect private (RFC 1918) or link-local (RFC 5735) IPs
# Returns 0 for public IP, 1 for private IP and 2 if IP
# is empty or has invalid format
ip_public() {
# format check (not checking >255)
local ip="$(echo "$1" | sed -rn '/^([0-9]{1,3}\.){3}[0-9]{1,3}$/p')"
[ -z "$ip" ] && return 2
# 10.0.0.0/8 (private), 192.168.0.0/16 (private), 169.254.0.0/16 (link-local)
(
[ "$ip" != "${ip#10.}" ] ||
[ "$ip" != "${ip#192.168.}" ] ||
[ "$ip" != "${ip#169.254.}" ]
) && return 1
# 172.16.0.0/12 (private)
[ "$ip" == "${ip#172.}" ] && return 0
ip=$(echo $ip | cut -d '.' -f 2)
[ $ip -ge 16 ] && [ $ip -le 31 ] && return 1 || return 0
}
via_ctlm() {
# Determine ip address only from ctlmgr_ctl
IP=$(ctlmgr_ctl r dslstatistic status/ifacestat0/ipaddr)
ip_public "$IP" || return 1
}
via_dsld() {
# Firmware ca. 04.68 and newer should be safe, but some boxes had it since 04.31
IP="$(/sbin/showdsldstat 2>/dev/null | sed -nr 's/0:.*ip ([0-9.]+).*/\1/p')"
ip_public "$IP" || return 1
}
via_env() {
# If multid (or whoever) has already determined the external IP, use it
IP="$IPADDR"
ip_public "$IP" || return 1
}
# AVM's home-brew NAT does not expose the external IP on network interface
# "dsl", but sets a route to it. This method should work on most DSL boxes,
# with two known exceptions:
# a) DNS servers are configured manually to hosts with public IP address.
# In this case multiple candidate IPs would be found. Thus we return an
# error rather than trying to guess which IP might be correct.
# b) If the box does not connect to DSL via PPPoE but uses the DSL modem as a
# bridge to e.g. a public /21 network, the external IP is not listed in
# the routing table at all, but the /21 network instead.
# There might be more exceptions in case of routing table manipulation (maybe
# for VPNs or if for any reason a single external host IP is explicitly added
# to the routing table).
via_route() {
local candidate_count=0
for ip in $(route -n | sed -nr 's/^([1-9][0-9]*(\.[0-9]+){3}) +0(\.0){3} +255(\.255){3} +.* dsl$/\1/p'); do
ip_public "$ip" && IP="$ip" && candidate_count=$((candidate_count + 1))
done
[ $candidate_count -eq 1 ] && return 0
unset IP
return 1
}
# On IP clients or UMTS, we are mostly behind a NAT and the only way to
# determine the external IP is to get the information from an external
# server. The most efficient method is STUN (stun-ip applet will try 3x).
# ip is cached for max 1 minute
via_stun() {
IP=$(find /tmp/.get_ip -mmin 0 -exec cat {} ';' 2>/dev/null)
[ -n "$IP" ] && return 0
IP=$(stun-ip $MOD_GET_IP_STUN)
[ $? -eq 0 -a -n "$IP" ] && echo "$IP" > /tmp/.get_ip && return 0
return 1
}
via_webcm() {
local queryfile="/usr/www/all/html/query.txt"
local querystring=""
if which ctlmgr_ctl >/dev/null; then
# Firmware ca. 04.76 and newer
IP=$(ctlmgr_ctl r connection0 pppoe:status/ip)
else
if [ "$(sed -n '/var:n\[/p' $queryfile)" ]; then
# Firmware ca. 04.84 and newer (should never be used, see above)
querystring="var:n[0]=connection0:pppoe:status/ip"
else
# Older firmware
querystring="var:cnt=1&var:n0=connection0:pppoe:status/ip"
fi
IP="$(/usr/www/html/cgi-bin/webcm "getpage=${queryfile}&${querystring}")"
fi
# ctlmgr_ctl return values [box=val]: 7170=176, 7270=177, 7141=172
# Caveat: must use "-o" instead of "||", otherwise "$?" would be reset
[ $? -eq 0 -o $? -ge 170 ] && ip_public "$IP" && return 0 || return 1
}
via_ctlm6() {
#use ctlmgr_ctl for determining the IPv6 address
IP=$(ctlmgr_ctl r ipv6 status/ip)
}
via_ctlmpre6() {
# use cltmgr_ctl for determing the IPv6 prefix
IP=$(ctlmgr_ctl r ipv6 status/prefix | sed 's/\/.*//g')
}
via_dsld6() {
# use showdsldstat for determining the IPv6 address
IP=$(showdsldstat | sed -rn 's/.* IPv6: address ([^/]*).*/\1/p')
}
via_dsldpre6() {
# use showdsldstat for determining the IPv6 prefix
IP=$(showdsldstat | sed -rn 's/.* IPv6: prefix ([^ ]*).*/\1/p')
}
# Set user-defined method (e.g. via web UI) if no argument is given
[ $# -eq 0 ] && method="$MOD_GET_IP_METHOD" || method="$1"
case $method in
-h|--help)
helpmsg
exit 0
;;
-a|--all|""|--extquery|-o|--ostat)
# for compatibility reason only, may be removed later
[ "$method" ] && [ "$method" != "-a" ] && [ "$method" != "--all" ] &&
echo "warning: method $method is obsolete, using --all instead" >&2
# Why this order?
# 1.) STUN should always work and is fast
# 2.) route is fast, works in all firmwares, problematic routing configs are rare
# 3.) dsld is faster than ctlmgr/webcm, but not always available
# 4.) ctlmgr/webcm is slow, but should work in all firmwares on DSL boxes
for mode in stun route dsld webcm; do
via_$mode
[ $? -eq 0 ] && break
done
;;
-c|--ctlm)
via_ctlm
;;
-d|--dsld)
via_dsld
;;
-e|--env)
via_env
;;
-r|--route)
via_route
;;
-s|--stun)
via_stun
;;
-w|--webcm)
via_webcm
;;
-c6|--ctlm6)
via_ctlm6
;;
-d6|--dsld6)
via_dsld6
;;
-cp6|--ctlmpre6)
via_ctlmpre6
;;
-dp6|--dsldpre6)
via_dsldpre6
;;
*)
helpmsg >&2
exit 1
;;
esac
[ $? -ne 0 ] && echo "get_ip error" >&2 && exit 1
[ -z $IP ] && echo "get_ip error: no ip address found" >&2 && exit 1
echo "$IP"