I want to configure a freeradius server in the way that an authentication is successful only if NAS-IP-Address attribute is not empty and equals to some specific IP (of course a user name and a password match).
How should I do it? I have tried to read the documentation without success:
http://freeradius.org/rfc/attributes.html
http://freeradius.org/rfc/rfc2865.html#NAS-IP-Address
Sure, there are many ways of doing this.
authorize {
if (!NAS-IP-Address) {
reject
}
if (NAS-IP-Address != 192.168.0.2) {
reject
}
if ("%{sql:SELECT count(*) FROM table WHERE User-Name = '%{User-Name}' AND IP-Address = '%{NAS-IP-Address}'" == 0) {
reject
}
}
In v3.0.x subnet matching is also supported, where < > are reassigned to mean the set operators (< subset of) (> superset of).
if (!(<ipv4prefix>NAS-IP-Address < 192.168.0.0/16)) {
reject
}
NAS-IP-Address = 192.168.0.2
(0) ? if (<ipv4prefix>NAS-IP-Address < 192.168.0.0/16)
(0) ? if (<ipv4prefix>NAS-IP-Address < 192.168.0.0/16) -> TRUE
NAS-IP-Address = 192.169.0.2
(0) ? if (<ipv4prefix>NAS-IP-Address < 192.168.0.0/16)
(0) ? if (<ipv4prefix>NAS-IP-Address < 192.168.0.0/16) -> FALSE
Related
I have implemented user provisioning/deprovisioning with SCIM like so :
users_controller.rb
class Scim::UsersController < Scim::ScimController
before_action :set_scim_provider
def index
startIndex = params[:startIndex].to_i
startIndex = 1 if startIndex < 1# if the user send a startIndex < 1, it is bad data, we don't take it.
itemsPerPage = params[:count].to_i
if itemsPerPage < 1 || itemsPerPage > #scim_provider.max_results
itemsPerPage = #scim_provider.default_number_of_results
end
scim_users = #scim_provider.identity_provider.communaute_accesses.from_scim
if params["filter"]
parser = Scim::QueryFilter::Parser.new
rpn_array = parser.parse(params["filter"])
tree = parser.tree
if tree.length == 3 and tree[0]== 'eq' and tree[1] == 'userName'
userName = tree[2]
scim_users = scim_users.where(provider_identifier: userName.delete('"'))
else
fail 'e'
end
end
paginated_users = scim_users.order(:created_at).offset(startIndex - 1).limit(itemsPerPage)
r = {
"schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"totalResults": scim_users.size,
"Resources": paginated_users.map { |ca| #scim_provider.representation_for_user(ca) },
"startIndex": startIndex,
"itemsPerPage": itemsPerPage
}
render_json_result(r, 200)
end
def create
if #scim_provider.identity_provider.communaute_accesses.from_scim.find_by(provider_identifier: #body_params['userName'])
render_409_conflict("uniqueness")
else
ca = #scim_provider.identity_provider.communaute_accesses.find_by(provider_identifier: #body_params['userName'], communaute_id: #scim_provider.identity_provider.communaute.id)
if ca.nil?
ca = #scim_provider.identity_provider.communaute_accesses.create(provider_identifier: #body_params['userName'], communaute_id: #scim_provider.identity_provider.communaute.id)
end
ca.update_last_raw_value("scim", #body_string)
ca.extract_values_from_scim
ca.queue_send
end
render_json_result(#scim_provider.representation_for_user(ca), 201)
end
def show
user = #scim_provider.identity_provider.communaute_accesses.from_scim.find_by(provider_identifier: #body_params['userName'])
if user
render_json_result(#scim_provider.representation_for_user(user), 200)
else
render_404_not_found(params[:id])
end
end
def update
ca = #scim_provider.identity_provider.communaute_accesses.from_scim.find_by(provider_identifier: #body_params['userName'])
uc = UserCommunaute.find_by(provider_identifier: #body_params['userName'])
ca.update_last_raw_value("scim", #body_string)
ca.extract_values_from_scim
unless ca.nil?
if ca.pending?
ca.update_last_raw_value("scim", #body_string)
ca.update(active: false)
if ca.active == false
fail "Unable to delete this user because of activeness" if ca.active == true
ca.destroy!
end
render_json_result(#scim_provider.representation_for_communaute_access_patch(ca), 200)
end
end
unless uc.nil?
uc.update(active: #body_params['active'])
if uc.active == false
uc.user.communaute_accesses.from_scim.destroy_all
uc.user.user_communautes.from_scim.destroy_all
render_json_result(#scim_provider.representation_for_user_communaute_patch(uc), 200)
end
end
end
end
Explanations:
When updating a user, SCIM sends a PATCH request like this:
{"schemas"=>["urn:ietf:params:scim:api:messages:2.0:PatchOp"], "Operations"=>[{"op"=>"Replace", "path"=>"active", "value"=>"False"}]} (#body_params in the code)
Which is what i am expecting. But, for a while, i was receiving the userName also in the body response during the PATCH operation.
This is how I fetch the correct user in my DB.
Actual result:
I don't receive the userName anymore when SCIM hits my update action.
Expected results:
Being able to receive information about the user during the PATCH operation to fetch the userName and find the right user in my database.
I have tried almost everything. When SCIM hits the index action, which it does everytime before going anywhere else, it does return me a userName et everything ends up as a 200 OK.
Then, when passing through update, it sends me nothing.
What I have tried last is to isolate the userName as an instance variable in the index action to fetch it after in the update like so:
# index
...
if params["filter"]
parser = Scim::QueryFilter::Parser.new
rpn_array = parser.parse(params["filter"])
tree = parser.tree
if tree.length == 3 and tree[0]== 'eq' and tree[1] == 'userName'
#user_name = tree[2]
scim_users = scim_users.where(provider_identifier: #user_name.delete('"'))
else
fail 'e'
end
end
...
# update
def update
ca = #scim_provider.identity_provider.communaute_accesses.from_scim.find_by(provider_identifier: #user_name)
uc = UserCommunaute.find_by(provider_identifier: #user_name)
ca.update_last_raw_value("scim", #body_string)
ca.extract_values_from_scim
...
But, #user_name in update seems to disappear as its value is nil.
I am deprovisioning from Azure Active Directory and Okta in a production environment.
Mapping is ok in both platforms.
Provisioning is working like a charm.
Please refer to https://developer.okta.com/docs/reference/scim/scim-20/#update-a-specific-user-patch for PATCH /Users/{userId}. Could you not make use of the userId in the url to identify the user ?
My Freeradius server is supposed to deny users access which have exceeded their usage limit, but the server accepts them, while the return message that users have exceeded their limit, is being set anyway.
Debug log:
rad_recv: Access-Request packet from host 1.2.3.4 port 46010, id=13, length=197
NAS-Port-Type = Wireless-802.11
Calling-Station-Id = "80:ED:2C:E5:EB:C6"
Called-Station-Id = "hotspot1"
NAS-Port-Id = "bridge"
User-Name = "USERNAME"
NAS-Port = 2151677955
Acct-Session-Id = "80400003"
Framed-IP-Address = 192.168.8.251
Mikrotik-Host-IP = 192.168.8.251
CHAP-Challenge = 0xa484e5a94500de0751545d5a69777d03
CHAP-Password = 0xb99d22e3c7c8cef532b70f9f514eef029c
Service-Type = Login-User
WISPr-Logoff-URL = "http://192.168.8.1/logout"
NAS-Identifier = "ROUTER"
NAS-IP-Address = 10.0.0.114
# Executing section authorize from file /etc/freeradius/sites-enabled/default
+group authorize {
++[preprocess] = ok
[chap] Setting 'Auth-Type := CHAP'
++[chap] = ok
++[mschap] = noop
++[digest] = noop
[suffix] No '#' in User-Name = "USERNAME", looking up realm NULL
[suffix] No such realm "NULL"
++[suffix] = noop
[eap] No EAP-Message, not doing EAP
++[eap] = noop
++[files] = noop
[sql] expand: %{User-Name} -> USERNAME
[sql] sql_set_user escaped user --> 'USERNAME'
rlm_sql (sql): Reserving sql socket id: 31
[sql] expand: SELECT id, username, attribute, value, op FROM radcheck WHERE username = '%{SQL-User-Name}' ORDER BY id -> SELECT id, username, attribute, value, op FROM radcheck WHERE username = 'USERNAME' ORDER BY id
rlm_sql_mysql: query: SELECT id, username, attribute, value, op FROM radcheck WHERE username = 'USERNAME' ORDER BY id
[sql] User found in radcheck table
[sql] expand: SELECT id, username, attribute, value, op FROM radreply WHERE username = '%{SQL-User-Name}' ORDER BY id -> SELECT id, username, attribute, value, op FROM radreply WHERE username = 'USERNAME' ORDER BY id
rlm_sql_mysql: query: SELECT id, username, attribute, value, op FROM radreply WHERE username = 'USERNAME' ORDER BY id
[sql] expand: SELECT groupname FROM radusergroup WHERE username = '%{SQL-User-Name}' ORDER BY priority -> SELECT groupname FROM radusergroup WHERE username = 'USERNAME' ORDER BY priority
rlm_sql_mysql: query: SELECT groupname FROM radusergroup WHERE username = 'USERNAME' ORDER BY priority
rlm_sql (sql): Released sql socket id: 31
++[sql] = ok
++[expiration] = noop
++[logintime] = noop
[pap] WARNING: Auth-Type already set. Not setting to PAP
++[pap] = noop
rlm_sqlcounter: Entering module authorize code
rlm_sqlcounter: Could not find Check item value pair
++[dailycounter] = noop
++? if (reject)
? Evaluating (reject) -> FALSE
++? if (reject) -> FALSE
rlm_sqlcounter: Entering module authorize code
rlm_sqlcounter: Could not find Check item value pair
++[weeklycounter] = noop
++? if (reject)
? Evaluating (reject) -> FALSE
++? if (reject) -> FALSE
rlm_sqlcounter: Entering module authorize code
rlm_sqlcounter: Could not find Check item value pair
++[monthlycounter] = noop
++? if (reject)
? Evaluating (reject) -> FALSE
++? if (reject) -> FALSE
rlm_sqlcounter: Entering module authorize code
rlm_sqlcounter: Could not find Check item value pair
++[noresetcounter] = noop
++? if (reject)
? Evaluating (reject) -> FALSE
++? if (reject) -> FALSE
rlm_sqlcounter: Entering module authorize code
rlm_sqlcounter: Could not find Check item value pair
++[dailyBytecounter] = noop
++? if (reject)
? Evaluating (reject) -> FALSE
++? if (reject) -> FALSE
rlm_sqlcounter: Entering module authorize code
rlm_sqlcounter: Could not find Check item value pair
++[weeklyBytecounter] = noop
++? if (reject)
? Evaluating (reject) -> FALSE
++? if (reject) -> FALSE
rlm_sqlcounter: Entering module authorize code
sqlcounter_expand: 'SELECT (IFNULL(SUM(AcctInputOctets), 0) + IFNULL(SUM(AcctOutputOctets), 0)) as used_data FROM (SELECT * FROM radacct b WHERE username = '%{User-Name}' AND dateformat_ym(acctstarttime) = dateformat_ym(CURDATE()) GROUP BY acctuniqueid) a'
[monthlyBytecounter] expand: SELECT (IFNULL(SUM(AcctInputOctets), 0) + IFNULL(SUM(AcctOutputOctets), 0)) as used_data FROM (SELECT * FROM radacct b WHERE username = '%{User-Name}' AND dateformat_ym(acctstarttime) = dateformat_ym(CURDATE()) GROUP BY acctuniqueid) a -> SELECT (IFNULL(SUM(AcctInputOctets), 0) + IFNULL(SUM(AcctOutputOctets), 0)) as used_data FROM (SELECT * FROM radacct b WHERE username = 'USERNAME' AND dateformat_ym(acctstarttime) = dateformat_ym(CURDATE()) GROUP BY acctuniqueid) a
WARNING: Please replace '%S' with '${sqlmod-inst}'
sqlcounter_expand: '%{sql:SELECT (IFNULL(SUM(AcctInputOctets), 0) + IFNULL(SUM(AcctOutputOctets), 0)) as used_data FROM (SELECT * FROM radacct b WHERE username = 'USERNAME' AND dateformat_ym(acctstarttime) = dateformat_ym(CURDATE()) GROUP BY acctuniqueid) a}'
[monthlyBytecounter] sql_xlat
[monthlyBytecounter] expand: %{User-Name} -> USERNAME
[monthlyBytecounter] sql_set_user escaped user --> 'USERNAME'
[monthlyBytecounter] expand: SELECT (IFNULL(SUM(AcctInputOctets), 0) + IFNULL(SUM(AcctOutputOctets), 0)) as used_data FROM (SELECT * FROM radacct b WHERE username = 'USERNAME' AND dateformat_ym(acctstarttime) = dateformat_ym(CURDATE()) GROUP BY acctuniqueid) a -> SELECT (IFNULL(SUM(AcctInputOctets), 0) + IFNULL(SUM(AcctOutputOctets), 0)) as used_data FROM (SELECT * FROM radacct b WHERE username = 'USERNAME' AND dateformat_ym(acctstarttime) = dateformat_ym(CURDATE()) GROUP BY acctuniqueid) a
[monthlyBytecounter] expand: /var/log/freeradius/sqltrace.sql -> /var/log/freeradius/sqltrace.sql
rlm_sql (sql): Reserving sql socket id: 30
rlm_sql_mysql: query: SELECT (IFNULL(SUM(AcctInputOctets), 0) + IFNULL(SUM(AcctOutputOctets), 0)) as used_data FROM (SELECT * FROM radacct b WHERE username = 'USERNAME' AND dateformat_ym(acctstarttime) = dateformat_ym(CURDATE()) GROUP BY acctuniqueid) a
[monthlyBytecounter] sql_xlat finished
rlm_sql (sql): Released sql socket id: 30
[monthlyBytecounter] expand: %{sql:SELECT (IFNULL(SUM(AcctInputOctets), 0) + IFNULL(SUM(AcctOutputOctets), 0)) as used_data FROM (SELECT * FROM radacct b WHERE username = 'USERNAME' AND dateformat_ym(acctstarttime) = dateformat_ym(CURDATE()) GROUP BY acctuniqueid) a} -> 3111228361
rlm_sqlcounter: (Check item - counter) is less than zero
rlm_sqlcounter: Rejected user USERNAME, check_item=1048576000, counter=3111228361
++[monthlyBytecounter] = reject
++? if (reject)
? Evaluating (reject) -> TRUE
++? if (reject) -> TRUE
++if (reject) {
+++update reply {
+++} # update reply = noop
++} # if (reject) = noop
rlm_sqlcounter: Entering module authorize code
rlm_sqlcounter: Could not find Check item value pair
++[noresetBytecounter] = noop
++? if (reject)
? Evaluating (reject) -> FALSE
++? if (reject) -> FALSE
+} # group authorize = ok
Found Auth-Type = CHAP
# Executing group from file /etc/freeradius/sites-enabled/default
+group CHAP {
[chap] login attempt by "USERNAME" with CHAP password
[chap] Using clear text password "PASSWORD" for user USERNAME authentication.
[chap] chap user USERNAME authenticated succesfully
++[chap] = ok
+} # group CHAP = ok
# Executing section post-auth from file /etc/freeradius/sites-enabled/default
+group post-auth {
[sql] expand: %{User-Name} -> USERNAME
[sql] sql_set_user escaped user --> 'USERNAME'
[sql] expand: %{User-Password} ->
[sql] ... expanding second conditional
[sql] expand: %{Chap-Password} -> 0xb99d22e3c7c8cef532b70f9f514eef029c
[sql] expand: INSERT INTO radpostauth (username, pass, reply, authdate) VALUES ( '%{User-Name}', '%{%{User-Password}:-%{Chap-Password}}', '%{reply:Packet-Type}', '%S') -> INSERT INTO radpostauth (username, pass, reply, authdate) VALUES ( 'USERNAME', '0xb99d22e3c7c8cef532b70f9f514eef029c', 'Access-Accept', '2017-08-31 10:59:03')
[sql] expand: /var/log/freeradius/sqltrace.sql -> /var/log/freeradius/sqltrace.sql
rlm_sql (sql) in sql_postauth: query is INSERT INTO radpostauth (username, pass, reply, authdate) VALUES ( 'USERNAME', '0xb99d22e3c7c8cef532b70f9f514eef029c', 'Access-Accept', '2017-08-31 10:59:03')
rlm_sql (sql): Reserving sql socket id: 29
rlm_sql_mysql: query: INSERT INTO radpostauth (username, pass, reply, authdate) VALUES ( 'USERNAME', '0xb99d22e3c7c8cef532b70f9f514eef029c', 'Access-Accept', '2017-08-31 10:59:03')
rlm_sql (sql): Released sql socket id: 29
++[sql] = ok
++[exec] = noop
+} # group post-auth = ok
Sending Access-Accept of id 13 to 1.2.3.4 port 46010
Mikrotik-Total-Limit = 1048576000
Reply-Message = "You have exceeded your usage limit this month."
It appears that the usage limit is being checked correctly, but somehow the return is set to accept?
Sending Access-Accept of id 13 to 1.2.3.4 port 46010
Mikrotik-Total-Limit = 1048576000
Reply-Message = "You have exceeded your usage limit this month."
The configuration of the specific part in sites-enabled/default is this:
monthlyBytecounter {
reject = 1
}
if (reject) {
update reply {
Reply-Message := "You have exceeded your usage limit this month."
}
reject
}
I'm running freeradius: FreeRADIUS Version 2.2.8 on Ubuntu 16.04 LTS
Any ideas what may cause the problem ?
Could be something wrong with monthlyBytecounter in counter.conf
Mine is here (daily)
sqlcounter counterChilliSpotMaxTotalOctetsDaily {
counter-name = ChilliSpot-Max-Total-Octets-Daily
check-name = CS-Total-Octets-Daily
counter-type = data
reply-name = ChilliSpot-Max-Total-Octets
sqlmod-inst = sql
key = User-Name
reset = daily
query = "SELECT IFNULL((SUM(AcctInputOctets + AcctOutputOctets)),0) FROM radacct WHERE UserName='%{%k}' AND UNIX_TIMESTAMP(AcctStartTime) + AcctSessionTime > '%b'"
}
But I do define limit in radcheck for CS-Total-Octets-Daily for the specific user.
You are returning only monthlyBytecounter reply message, not the monthlyBytecounter return value. In case of exceeded usage limit, you must return 0(zero) or send disconnection request like below.
echo \"User-Name='$username'\" | radclient -x -c 1 -n 3 -r 3 -t 3 '127.0.0.1:3997' 'disconnect' 'testing123'
Post your monthlyBytecounter code if this does not solve your question
st = 'pen'
ak = '123123'
agreements = Client.where{authentication_key == ak}.first.agreements
products = Product.joins{agreements}.where{agreements.id.in(a) & (short_description.like(st) | long_description.like( st))}
I am trying with the above but I need the matched agreement in my result set too..
Because of this
class Product < ActiveRecord::Base
has_and_belongs_to_many :agreements, uniq: true
I cant use products.first.agreement.first.... That could be a different agreement.
I'm not sure if this is exactly what you need, but hopefully it will be:
client = Client.where { authentication_key == ak }.first
products = Client.agreements.joins { products }.where { (short_description.like(st) | long_description.like(st) }
This returns the products associated with the agreements which are, in turn, associated to your client. If you need to get one client's agreements for a product, you could write a scope:
class Product < ActiveRecord::Base
#...
def self.agreements_for_client(id)
joins { agreements }.where { client_id.eq(id) }
end
end
and the call it:
client = Client.where { authentication_key == ak }.first
Product.first.agreements_for_client(client.id)
I hope this helps.
I'm doing the following:
email = 'bob#luv.southwest.com'
domain_rules = [/craigslist.org/, /evite.com/, /ziprealty.com/, /alleyinsider.com/, /fedexkinkos.com/, /luv.southwest.com/, /fastsigns.com/, /experts-exchange.com/, /feedburner.com/]
user, domain = email.split('#')
domain_rules.each { |rule| return true if !domain.match(rule).nil? }
Problem is this is case sensitive. Is there a way to make this all case insensative, without having to add /i to the end of every single rule?
Use the option "i" (ignore case)
domain_rules = [
/craigslist.org/i,
/evite.com/i,
/ziprealty.com/i,
/alleyinsider.com/i,
/fedexkinkos.com/i,
/luv.southwest.com/i,
/fastsigns.com/i,
/experts-exchange.com/i,
/feedburner.com/i
]
test it here... http://rubular.com/
downcase the email & domain you want to match first, then find_all regexp matches.
You can use find to only retrieve the first matching "rule".
email = 'bob#luv.southwest.com'
domain_rules = [/craigslist.org/, /evite.com/, /ziprealty.com/, /alleyinsider.com/, /fedexkinkos.com/, /luv.southwest.com/, /fastsigns.com/, /experts-exchange.com/, /feedburner.com/]
user, domain = email.split('#').collect { |s| s.downcase }
p domain_rules.find_all { |rule| domain[rule] }
There's also no real need for Regexp:
email = 'bob#luv.southwest.com'
matchable_domains = %w{ craigslist.org evite.com ziprealty.com alleyinsider.com fedexkinkos.com luv.southwest.com fastsigns.com experts-exchange.com feedburner.com }
user, domain = email.downcase.split('#')
p matchable_domains.find_all { |rule| matchable_domains.include?(domain) }
Or, you can do ONLY Regexp:
email = 'bob#luv.southwest.com'
regexp = /[A-Z0-9._%+-]+#(craigslist\.org|evite\.com|ziprealty\.com|alleyinsider\.com|fedexkinkos\.com|luv\.southwest\.com|fastsigns\.com|experts-exchange\.com|feedburner\.com)/
p regexp === email # => true
p regexp.match(email) # => #<MatchData "bob#luv.southwest.com" 1:"bob" 2:"luv.southwest.com">il
No need to use regexes for simple string comparisons.
email = 'bob#luv.southwest.com'
domains = %w(CraigsList.org evite.com ZiPreAltY.com alleyinsider.com fedexkinkos.com luv.southwest.com fastsigns.com experts-exchange.com feedburner.com)
user, user_domain = email.split('#')
p domains.any? { |domain| domain.casecmp(user_domain).zero? }
String#casecmp does a case-insensitive comparison.
You could just make the email address lowercase.
One problem I see with your current implementation is that it will match domains like luvesouthwestlcom.com, because . matches any character. You could deal with this by escaping all the url you are using by doing something like this:
email = 'bob#luv.southwest.com'
domains = %w[craigslist.org evite.com ziprealty.com alleyinsider.com fedexkinkos.com luv.southwest.com fastsigns.com experts-exchange.com feedburner.com]
domain_rules = domains.map{|d| /#{Regexp.escape(d)}/i }
user, domain = email.split('#')
domain_rules.any? { |rule| domain.match(rule) }
Also, if you are only looking for exact matches, you don't really need regular expressions and could just check to see if the email's domain includes one of the strings you are looking for.
email = 'bob#luv.southwest.com'
domains = %w[craigslist.org evite.com ziprealty.com alleyinsider.com fedexkinkos.com luv.southwest.com fastsigns.com experts-exchange.com feedburner.com]
user, domain = email.split('#')
domain.downcase! # lower cases the string in place
domains.any? { |rule| domain.include?(rule) }
The issue with either of these is that they will match anything with an exact string in it, so 'craigslist.org' will match 'nyc.craiglist.org' and 'craigslist.org.uk'. If you want exact matches, you could just use == after downcasing your input domain. e.g.
domains.any? { |rule| domain == rule }
You could pass the rules as simple strings and construct the regex on the fly:
email = 'bob#luv.southwest.com'
domains = %w(craigslist.org evite.com ziprealty.com) # etc
user, domain = email.split('#').collect { |s| s.downcase }
p domains.any? { |d| domain.match(/#{d}/i) }
I had code like this in Ruby:
#clientipaddress = request.env["HTTP_CLIENT_IP"]
if (#clientipaddress == nil)
#clientipaddress = request.env["HTTP_X_FORWARDED_FOR"]
end
if (#clientipaddress == nil)
#clientipaddress = request.env["REMOTE_ADDR"]
end
if (#clientipaddress != nil)
comma = #clientipaddress.index(",")
if (comma != nil && comma >= 0)
#clientipaddress = #clientipaddress[0, comma]
end
end
It took care of all the possible ways that the IP might show up. For instance, on my local development machine, there is no proxy. But in QA and Production the proxies are there, and sometimes they provide more than one address.
I don't need to know the Groovy syntax, just which methods get me the equivalent of the three different ways I ask for the IP above.
I think this should be what you want:
request.getRemoteAddr()
request.getHeader("X-Forwarded-For")
request.getHeader("Client-IP")
//action in controller
def postentry (accountno) {
def fulldata = request.reader.text
def remoteadd = request.getRemoteAddr()
println "ip request "+remoteadd
...
}
result ---ip request 0:0:0:0:0:0:0:1