笔记:使用python发送COA报文动态改变RADIUS用户属性

1,927 阅读5分钟

blog.sina.com.cn/s/blog_5d18…

这几天在研究如何使用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查看用户的速率属性,确实是已经改过来了!