通过ipro实现ldap服务器的负载
  

江威 5888

{{ttag.title}}
本帖最后由 江威 于 2016-12-1 17:45 编辑

--[[
做法是保存认证消息,然后根据消息类型做读写分离;
在分离到不同的服务器之前,发送认证消息,并保存当前请求;
当接收到认证应答后,发送之前保存的请求。

LDAP的PDU使用ASN1进行编码
第一个字节必然是0x30(00 1 11000)
第二个字节是短编码长度,或者长编码的字节数
    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
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | Always 0x30   | Total Len ... | Always 0x02   | MsgID Len ... |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |    MsgID ...  |  Protocol Op  |  Msg Len ...  | Always 0x02   |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | LDAP Ver Len  |  LDAP Ver ... | Always 0x04   | BINDDN Len ...|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |   BIND DN ... | Always 0x80   | Password Len  | Password ...  |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
主要是看消息类型
TCP.enable_mblb();
--用来分辨读写操作
local ldap_write_class={[6]=true,[8]=true,[10]=true,[12]=true};
--这个数组用来记录支持的消息类型
local ldap_msg_types={
           [0]="bind request",
           [1]= "bind response",
           [2]= "unbind request",
           [3]= "search request",
           [4]= "search response",
           [6]= "modify request",
           [7]= "modify response",
           [8]= "add request",
           [9]= "add response",
          [10]= "delete request",
          [11]= "delete response",
          [12]= "modifydn request",
          [13]= "modifydn response",
          [14]= "compare request",
          [15]= "compare response",
          [16]= "abandon request",
          [23]= "extended request",
          [24]= "extended response",
}
--读写节点池
local switch_map={["read_pool"]="pool_r",["write_pool"]="pool_w"}
--调试日志开关
local debug_log_switch=true;


local rebind=false;
local binding="";--保存的认证消息
local replayop="";--切换节点池而保存的其他消息
local writing=false;

--这函数只返回消息类型
local function ldap_parse(msg)
        local ldap_magic,next_off=struct.unpack(">I1",msg);
        if ldap_magic~=0x30 then
                return nil;
        end
        --看第二个字节的最高位是否为0
        --是则跳过(len_bytes&127)个字节
        local len_bytes=0;
        len_bytes,next_off=struct.unpack(">I1",msg,next_off);
        if len_bytes>128 then
                len_bytes=len_bytes-128;
                next_off=next_off+len_bytes;
        end
       
        local msg_type,msg_len,msg_id=0;
        msg_type,msg_len,next_off=struct.unpack(">I1I1",msg,next_off);
        --仍然要判断长度是长编码还是短编码
        if msg_len>128 then
                msg_len=msg_len-128;
                next_off=next_off+msg_len;
        else
                msg_len=1;
        end
        msg_id,next_off=struct.unpack(">I"..msg_len,msg,next_off);
        --终于到达请求类型的偏移了,第一个字节的低5位就是消息类型
        local msgtype=struct.unpack(">I1",msg,next_off);
        msgtype=bit.band(msgtype,31);
        return msgtype;
end
event CLIENT_ACCEPT{
        TCP.collect();
}
event CLIENT_DATA{
        --默认一个包里的数据就是一个完整的请求
        local payload=TCP.payload();
        local msgtype=ldap_parse(payload);
        if not msgtype then
                TCP.close();
        end
        if ldap_msg_types[msgtype] then
                if debug_log_switch then
                        LOG.log(LOG.DEBUG,"message type is :"..ldap_msg_types[msgtype]);
                end
        else
                if debug_log_switch then
                        LOG.log(LOG.DEBUG,"unknow message type:"..msgtype);
                end
                TCP.close();
        end
       
        --处理认证消息
        if msgtype==0 then
                writing=false;
                rebind=false;
                binding=payload;
                pool(switch_map["read_pool"]);
        --由读切换到写
        elseif ldap_write_class[msgtype] and writing==false then
                if debug_log_switch then
                        LOG.log(LOG.DEBUG,"rebind with ldap_write");
                end
                rebind=true;
                writing=true;
                replayop=payload;
                TCP.replace(1,TCP.offset()+1,binding)
                pool(switch_map["write_pool"]);
        --由写切换到读
        elseif not ldap_write_class[msgtype] and writing then
                rebind=true;
                writing=false;
                replayop=payload;
                TCP.replace(1,TCP.offset()+1,binding);
                pool(switch_map["read_pool"]);
        end
        TCP.release();
        TCP.collect();
       
}
event SERVER_CONNECTED{
        TCP.collect();
}
event SERVER_DATA{
        if rebind then
                rebind=false;
                local payload =TCP.payload();
                local msgtype=ldap_parse(payload);
                TCP.respond(replayop);
                --隔断其数据,不向客户端发
                TCP.replace(1,TCP.offset()+1,"");
                TCP.collect();
                return ;
        end
        TCP.release();
        TCP.collect();
}

打赏鼓励作者,期待更多好文!

打赏
2人已打赏

Sangfor_闪电回_朱丽 发表于 2016-12-2 09:09
  
厉害!
感谢楼主的分享,要是能把设备上的配置也截个图来,就更好啦!
Charles 发表于 2017-2-13 17:24
  
我表示编程都还给老师了
小维 发表于 2017-2-13 17:29
  
很感兴趣
adds 发表于 2017-2-13 22:02
  
if else end
for
龙腾风云 发表于 2017-2-14 08:31
  
完全不记得编程的东西了
梁木 发表于 2017-2-14 09:30
  

我表示编程都还给老师了
stonexia 发表于 2017-2-14 12:10
  
不明觉厉············
18803158001 发表于 2017-2-15 05:44
  
非常棒的东东,学习先
友谊之门 发表于 2017-2-15 08:34
  
厉害!!!!!
发表新帖
热门标签
全部标签>
每日一问
功能体验
技术盲盒
技术笔记
新版本体验
2023技术争霸赛专题
技术咨询
干货满满
标准化排查
产品连连看
GIF动图学习
安装部署配置
信服课堂视频
秒懂零信任
自助服务平台操作指引
技术晨报
深信服技术支持平台
答题自测
答题榜单公布
卧龙计划
通用技术
畅聊IT
云计算知识
排障笔记本
安全攻防
社区帮助指南
专家问答
技术圆桌
在线直播
MVP
网络基础知识
升级
上网策略
测试报告
日志审计
问题分析处理
流量管理
每日一记
运维工具
用户认证
原创分享
解决方案
sangfor周刊
VPN 对接
项目案例
SANGFOR资讯
专家分享
技术顾问
信服故事
SDP百科
功能咨询
终端接入
授权
设备维护
资源访问
地址转换
虚拟机
存储
迁移
加速技术
产品预警公告
玩转零信任
信服圈儿
S豆商城资讯
技术争霸赛
「智能机器人」
追光者计划
纪元平台
华北区拉练
天逸直播
以战代练
文档捉虫活动
山东区技术晨报
齐鲁TV
华北区交付直播
每周精选

本版达人

新手61940...

本周建议达人

zhao_HN

本周分享达人

ZSFKF

本周提问达人