这几天在研究如何使用RADIUS扩展,来动态改变用户属性。简单来说就是实现动态提速。 首先要搞清楚协议细节。在网上找了一下,发现这些RADISU扩展主要在RFC 3576和 RFC 5176这两个RFC文档定义
RADIUS扩展主要是为了动态改变用户属性,主要有两种:
- 一种是DM消息,用于中断用户连接,我们这边叫主动控制用户下线。
- 另一种则是COA消息,用于在用户不下线的情况下动态改变RADIUS用户的用户属性。我们这边主要用于动态提速业务,或者被称做夜间提速业务。
实际上这两种消息都跟原始的RADISU报文相似,但主要有以下不同:
1、端口号不同,DM和COA都使用UPD 3799。即我们向DAS(NAS或者说BAS)的UDP 3799端口发送报文,来实现相应的功能 2、整个报文跟RADISU记帐报文几乎完全一样,只是CODE字段不同,DM请求消息的CODE是41,COA请求消息的CODE是43,具体如下:
# Packet codes
AccessRequest = 1
AccessAccept = 2
AccessReject = 3
AccountingRequest = 4
AccountingResponse = 5
AccessChallenge = 11
StatusServer = 12
StatusClient = 13
DisconnectRequest = 40
DisconnectACK = 41
DisconnectNAK = 42
CoARequest = 43
CoAACK = 44
CoANAK = 45
3、不管是DM还是COA都要定位用户,根据RFC文档,需要定位NAS以及NAS上的SESSION。可通过多种属性来定位,但我用于测试的BAS是华为的ME60,经咨询只支持通过Acct-Session-Id 来定位具体的session(即用户)。下面的测试我就使用了Acct-Session-Id 和 NAS-IP-Address 分别定位 用户session和NAS
其次如何获取用户的Acct-Session-Id。这个方法很多,如可以使用计费抄送,copy一份所有的记帐消息;也可以把RADSIU报文镜像过来,自己从报文里面取;当然最方便的就是RADIUS服务器提供一个查询接口,可以直接获得指定用户的Acct-Session-Id。非常幸运,我们这边使用的RADIUS提供了查询页面,可以查询用户在线信息,其中就包括了用户的Acct-Session-Id
好了,通过上面的准备工作,万事具备只欠写代码了。
下载交安装pyradius,这个包里面提供了基本的RADIUS报文封装,可以极大的方便我们的工作
pyradius里面虽然定义了DM和COA的code值,但本身没有实现这两种消息,我们需要扩展一下pyradisu以便使其支持DM和COA。说实话这比较简单,只需要分别实现packet、host和client这三个类就行了,其中packet和host都可以直接继承原来的Packet和Host类,代码基本不用修改,只要增加一个COAport就可以了。Client麻烦一下,新的COAClient需要重写,但只绝大部分代码都可以直接复制原来Client的代码,只要在SendPacket函数里面改一下,调用之前定义的CoaPacket就可以了。甚至你都可以直接把Account的包直接当做COA来用,只要改端口号就可以了。我这里就不把具体的代码写出来了。
实现一个函数来发送COA报文(DM报文太简单了,这里面就不做介绍了,只COA几乎完全一样,只是不包括要改变的用户属性)
def sendCoa(srv,nas_ip,sessionID,up_speed,down_speed):
#速率单位为kbps,up_speed为上行速率,down_speed为下行速率
up_speed=up_speed*1024
down_speed=down_speed*1024
req=srv.CreateCoaPacket(code=pyrad.packet.CoARequest)
#下面这两个属性,NAS-IP-Address和Acct-Session-Id就不多说了,协议原理里面有介绍
req["NAS-IP-Address"] = nas_ip
req["Acct-Session-Id"] = sessionID
#下面这4种属性就是我们要更改的用户速率属性,以华为的ME60为例,其它BAS则请参照各自的文档。如要改变其它属性也请参照各自的文档
attrib=(2011,2) #上行承诺速率
req[attrib]=[struct.pack('>L', up_speed)]
attrib=(2011,3) #上行峰值速率
req[attrib]=[struct.pack('>L', up_speed)]
attrib=(2011,5) #下行承诺速率
req[attrib]=[struct.pack('>L', down_speed)]
attrib=(2011,6) #下行峰值速率
req[attrib]=[struct.pack('>L', down_speed)]
try:
print "Sending COA request"
reply=srv.SendPacket(req)
except pyrad.client.Timeout:
print "DAS(NAS or Bas) does not reply"
sys.exit(1)
except socket.error, error:
print "Network error: " + error[1]
sys.exit(1)
if reply.code==pyrad.packet.CoAACK:
print "Coa accepted"
elif reply.code==pyrad.packet.CoANAK:
print "Coa nak"
else:
print reply.code
print "Attributes returned by NAS:"
for i in reply.keys():
print "%s: %s" % (i, reply[i][0])
使用这个函数来发送COA消息,这个需要引用前面创建的类:
if __name__ == '__main__':
mydict=Dictionary("dictionary")#关于dictionary请参照我前面的文章
nasip='1.1.1.1' 这是BAS的IP地址
print "ceate client!"
srv=CoaClient(server=nasip,
secret="yourkey",
dict=mydict)
sessid="XX-XX-Z08202235000103b39764000023"
up=1024
down=5120
sendCoa(srv,nasip,sessid,up,down)
好了,我们运行试试。。。。。。,失败了,怎么回事?使用python发送COA报文动态改变RADIUS用户属性原来你没有在BAS上把你这台电脑IP加进去使用python发送COA报文动态改变RADIUS用户属性 这里以华为的ME60为例,登录BAS配置以下命令
radius-server authorization 1.1.1.155 shared-key yourkey
OK,重新运行,显示:
ceate client!
Sending COA request
Coa accepted
Attributes returned by NAS:
NAS-Identifier: xxxxxxxxxxxxxxxxxxxxxxxx
User-Name: xxxxxxxxxx@xxxxxx
NAS-IP-Address: xxxxxxxxxxxxxx
Acct-Session-Id: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
成功了,再登录到BAS查看用户的速率属性,确实是已经改过来了!