Radius 计费请求协议解析

        Radius 协议(Remote Authentication Dial-In User Service Protocol)是一种由 IETF(Internet Engineering Task Force)定义的网络协议,最初设计用于拨号网络的认证、授权和计费。它的工作流程为,由客户端发起拨号请求,网络设备将认证请求转发至 Radius 服务器,服务器根据用户信息进行认证和授权,之后将认证结果返回给网络设备,网络设备接到响应后决定用户是否有权访问网络。

        Radius 协议支持多种加密机制,可以为认证过程提供数据的安全保证,同时该协议还具备良好的扩展性,可以通过(Attribute-Value Pairs, AVPs)形式传递和夹带额外的信息,Radius 服务器是集中式的管理用户认证和授权信息,极大的简化了网络管理,总之在网络安全领域 Radius 服务扮演着机器重要的角色,确保只有授权用户才能访问其网络资源。

        Radius 协议分为两种模式:认证模式和计费模式。认证(Authentication)模式用于用户身份认证,由客户端发送一个包含用户凭证(用户名和密码)的认证请求(Access-Request)到 Radius 服务器,服务器根据认证结果返回认证接受(Access-Accept)消息或认证拒绝(Access-Reject)消息给用户。计费(Accounting)模式则用于记录用户对网络资源的使用情况,用于生成账单或进行网络使用审计,它通常在网络认证完成之后,网络设备定期向 Radius 服务器发送计费记录(Accounting-Request),包含用户登录时间、使用的字节数、断开连接的时间等,服务器收到计费消息后,会将记录保存起来,用于后续的计费和审计工作。

        下面是 Radius 协议数据格式摘要,Code 字段是一个八位字节,4 代表计费请求,5 代表计费应答;Identifier 字段是一个八位字节,用于匹配请求与回复;Length 字段占两个八位字节表示数据长度,取值介于 20 至 4096 之间;Authenticator 字段占十六个八位字节,根据请求或应答报文取值和意义各不相同;Attributes 为协议属性值,长度可变;

 消息结构
 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |     Code      |  Identifier   |            Length             |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |                                                               |
 |                         Authenticator                         |
 |                                                               |
 |                                                               |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |  Attributes ...
 +-+-+-+-+-+-+-+-+-+-+-+-+-

 Code 取值
     4       Accounting-Request
     5       Accounting-Response


 Attributes 结构
 0                   1                   2
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |     Type      |    Length     |  Value ...
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

 Type 取值
     1-39   (refer to RADIUS document [2])
    40      Acct-Status-Type
    41      Acct-Delay-Time
    42      Acct-Input-Octets
    43      Acct-Output-Octets
    44      Acct-Session-Id
    45      Acct-Authentic
    46      Acct-Session-Time
    47      Acct-Input-Packets
    48      Acct-Output-Packets
    49      Acct-Terminate-Cause
    50      Acct-Multi-Session-Id
    51      Acct-Link-Count
    60+     (refer to RADIUS document [2])

        Radius 协议数据格式大致了解了,接下来描述一下 Radius 计费协议的解析过程,编程语言为 C 语言。假设我们已经通过分光器或镜像方式采集到了用户 Radius 计费报文,准备从中提取业务需要的字段属性,下面是数据结构定义。

/* packet type define */
#define ACCESS_ACCEPT		2	    /* accounting request code */
#define ACCT_REQUEST		4	    /* accounting request code */
typedef struct tagRadiusPacket
{
    UCHAR m_ucCode;
    UCHAR m_ucIdentifier;
    USHORT m_usLength;
    char m_szAuthenticator[16];
}RADIUSPACKET_S;

/* packet attribute define */
#define USER_NAME		    1	    /* User-Name attribute */
#define NAS_IP_ADDR		    4	    /* NAS IP ADDRESS ATTRUBUTE */
#define ACCT_RESPONSE		5	    /* accounting response code */
#define FRAMED_IP_ADDRESS	8	    /* Framed-IP-Address attribute */
#define FILTER_ID		    11	    /* Filter-ID */
#define CALLED_PHONE		31	    /* Calling-Station-Id attribute */
typedef struct tagRadiusAttribute
{
    UCHAR m_ucType;
    UCHAR m_ucLength;
}RADIUSATTRIBUTE_S;

/* accept attribute define */
#define ACCT_STATUS_TYPE	40	    /* Account-Status-Type attribute */
#define ACCT_STATUS_START	1	    /* Account-Status is start */
#define ACCT_STATUS_STOP	2	    /* Account-Status is stop */
#define ACCT_STATUS_UPDATE	3	    /* specially for cisco's update status */
#define RADACCT_DGRAM_MAX	1024    /* largerest dgram packet size */

        下面是解析过程,解析操作只获取了用户名称、用户地址、电话号码和计费状态,其余的信息可以根据业务需要添加。

int parse(const u_char *pszPacket,int iPacketLength)
{
    ULONG ulStatus = 0;
    UINT uiPacketOffset = 0;
    UINT uiPacketLength = 0;
    UINT uiHeaderLength = 0;
    UINT uiAttributeNumber = 0;
    const struct ip *pstIP = NULL;
    const struct udphdr *pstUdp = NULL;
    const struct ether_header *pstEther = NULL;
    RADIUSPACKET_S *pstRadiusPacket = NULL;
    RADIUSATTRIBUTE_S *pstRadiusAttribute = NULL;
    RADIUSUSERINFORMATION_S stRadiusUserInformation;
    const int iRadiusAttributeSize = sizeof(RADIUSATTRIBUTE_S);

    memmove(szPacket,pszPacket,iPacketLength);
    memset(&stRadiusUserInformation,'\0',sizeof(RADIUSUSERINFORMATION_S));

    /* ether header */
    pstEther = (const struct ether_header *)szPacket;
    memcpy(stRadiusUserInformation.m_ucMac,pstEther->ether_shost,ETH_ALEN);

    /* ip header */
    pstIP = (struct ip *)(szPacket + sizeof(struct ether_header));
    if( pstIP->ip_p != IPPROTO_UDP ) return FAILURE;

    /* udp header */
    pstUdp = (const struct udphdr *)(szPacket + sizeof(struct ether_header) + pstIP->ip_hl * 4);
    if( ntohs(pstUdp->uh_dport) != g_pstIzpNdmsConfig->m_uiRadiusPort ) return FAILURE;

    /* radius packet */
    uiHeaderLength = (UINT)(sizeof(struct ether_header) + pstIP->ip_hl * 4 + sizeof(struct udphdr));
    pstRadiusPacket = (RADIUSPACKET_S *)(szPacket + uiHeaderLength);
    if( pstRadiusPacket->m_ucCode != ACCT_REQUEST ) return FAILURE;
    uiPacketLength = ntohs(pstRadiusPacket->m_usLength);
    if( uiPacketLength < sizeof(RADIUSPACKET_S) ) return FAILURE;

    /* RADIUS报文解析 */
    uiPacketOffset = sizeof(RADIUSATTRIBUTE_S);
    while( uiPacketLength - uiPacketOffset > sizeof(RADIUSATTRIBUTE_S) ) {
        if( ++ uiAttributeNumber > 70 ) break;
        pstRadiusAttribute = (RADIUSATTRIBUTE_S *)(szPacket + uiHeaderLength + uiPacketOffset);
        if( pstRadiusAttribute->m_ucLength < iRadiusAttributeSize ) continue;
        switch( pstRadiusAttribute->m_ucType ) {
            case USER_NAME:		memcpy(stRadiusUserInformation.m_szUserName,pstRadiusAttribute + iRadiusAttributeSize,pstRadiusAttribute->m_ucLength - iRadiusAttributeSize);
                break;
            case NAS_IP_ADDR:	memcpy((struct in_addr *)&stRadiusUserInformation.m_stNasIP,pstRadiusAttribute + iRadiusAttributeSize,pstRadiusAttribute->m_ucLength - iRadiusAttributeSize);
                break;
            case CALLED_PHONE:	memcpy(stRadiusUserInformation.m_szPhone,pstRadiusAttribute + iRadiusAttributeSize,pstRadiusAttribute->m_ucLength - iRadiusAttributeSize);
                break;
            case ACCT_STATUS_TYPE:	memcpy((ULONG *)&ulStatus,pstRadiusAttribute + iRadiusAttributeSize,sizeof(ULONG));
                stRadiusUserInformation.m_ulVerb = ntohl(ulStatus);
                break;
            case FRAMED_IP_ADDRESS:	memcpy((struct in_addr *)&stRadiusUserInformation.m_stUserIP,pstRadiusAttribute + iRadiusAttributeSize,pstRadiusAttribute->m_ucLength - iRadiusAttributeSize);
                break;
            default:		break;
        }
        uiPacketOffset += pstRadiusAttribute->m_ucLength;
    }

    return SUCCESS;
}

        以上就是 Radius 计费请求协议的解析实现,仅供参考。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

厉力文武

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值