Jump The Great Firewall【step15 支持DHCP】

一、修正错误

之前在测试过程中,经常性的出现Invalid msg错误,导致连接被重置。经过调查,发现原来是数据分片时最后一个分片的长度计算有误导致。下面我们来分析一下这个错误的代码:

int check_msg(client_t* client, msg_t* msg)
{
    size_t msg_data_len;
    if (msg->zone.clip)
    {
        if (msg->zone.last) msg_data_len = msg_data_length(msg) % client->max_length;
        else msg_data_len = client->max_length;
    }
    else msg_data_len = msg_data_length(msg);
    if (checksum(msg, sizeof(msg_t) + msg_data_len))
    {
        SYSLOG(LOG_ERR, "Invalid msg");
        return 0;
    }
    return 1;
}

由上面的代码可以看出,当到来的数据包为最后一个分片时,根据代码中的运算方法直接与对端的max_length取模,此时如果最后一个分片的长度正好能被max_length整除时。所计算出的长度就是错的,应此会平凡的遇到Invalid msg错误。与该错误类似的问题已全部修正,已修正的函数有:

network.c:
  process_clip_msg和check_msg函数
msg_group.c:
  parse_msg_group函数
server.c:
  tcp_process函数
client.c:
  client_process函数

二、支持DHCP

  1. 增加配置文件中的use_dhcp字段并增加lua接口
  2. 修改sys_login_msg_t结构
    typedef struct
    {
        unsigned short major_version;
        unsigned char  minor_version : 4;
        unsigned char  revision_version : 4;
        unsigned int   ip;
        unsigned int   gateway;
        unsigned char  mask;
        unsigned short internal_mtu;
        unsigned char  signature[31];
        unsigned char  dhcp;
    } sys_login_msg_t;
    

    将头3个字节修改为版本号的定义,在末尾增加31字节的signature和1字节的dhcp标志位,使用31字节作为signature的长度的意义在于与下一字节的dhcp标志位组合后使整个结构对齐到46字节。由于该校验工作只发生在连接建立后,因此出于安全和拓展因素考虑,在配置文件中只记录签名文件的路径,在每次进行校验时由lua脚本动态读出并做校验。这样做的好处是:(1) 安全,文件内容永远不记录在内存中 (2) 拓展性强,当服务器端添加新的客户端签名时无需重启服务器端程序。 需要注意的是:建议为每个单独的客户端配置不同的签名,这样做的好处是,每个客户端都独立持有各自的签名,更加安全。

  3. 修改server_process_login函数 1). 首先对客户端传来的版本号进行检查 2). 若不是DHCP模式则检查客户端传过来的内网IP是否在当前网段内 3). 检查客户端所传签名是否在当前签名文件中 4). 根据是否启用DHCP模式决定调用server_process_login_dhcp函数或server_process_login_no_dhcp函数,其中前者根据当前网段寻找一个合适的IP地址通知客户端,后者为原逻辑
  4. 修改connect_server函数 1). 检查服务器端传来的版本号与本地版本号是否相同 2). 检查服务器端传来的IP地址是否为0 3). 检查服务器端传来的signature是否与本地的相同 4). 检查服务器端传来的IP地址是否与本地的相同,若不同且服务器端为DHCP模式则使用服务器端给定的IP进行绑定 5). 进行其他初始化

三、更灵活的签名校验

为了兼容老版本,因此将sys_login_msg_t结构的头3字节用作版本号的定义。同时为了增强灵活性和安全性,在qtun程序中仅保存signature文件的路径,每次做校验时都动态的将其读出。

  1. 定义script_signature_verify和script_load_signature接口

    int script_signature_verify(lua_State* lua, const unsigned char signature[31]) {
        lua_getglobal(lua, "signature_verify");
        lua_pushlstring(lua, (char*)signature, 31);
        if (lua_pcall(lua, 1, 1, 0) != 0) {
            SYSLOG(LOG_ERR, "%s\n", lua_tostring(lua, -1));
            return 0;
        }
        return lua_toboolean(lua, -1);
    }
    
    int script_load_signature(lua_State* lua, unsigned char signature[31]) {
        const char* str;
        lua_getglobal(lua, "signature_load");
        if (lua_pcall(lua, 0, 1, 0) != 0) {
            SYSLOG(LOG_ERR, "%s\n", lua_tostring(lua, -1));
            return 0;
        }
        str = lua_tostring(lua, -1);
        memcpy(signature, str, 31);
        return 1;
    }
    
  2. 以上两个函数最终会调用qtun.lua中的signature_verify和signature_load接口
    function signature_load()
        local file = io.open(qtun.conf.signature_file)
        local ret = ‘‘
        assert(file)
        if qtun.state.is_server then
            ret = file:read(‘*a‘)
        else
            ret = file:read(31)
        end
        file:close()
        return ret
    end
    
    function signature_verify(signature)
        if qtun.state.is_server then
            local file = io.open(qtun.conf.signature_file)
            assert(file)
            while true do
                local line = file:read()
                if line == nil then break end
                if line == signature then return true end
            end
            file:close()
            return false
        else
            return signature == signature_load()
        end
    end
    
  3. 在connect_server函数中,创建login消息前先将签名读出来
    unsigned char signature[31] = {0};
    script_load_signature(qtun->lua, signature);
    msg = new_login_msg(qtun->localip, 0, 0, 1, 0, signature);
    
  4. 在server_process_login函数中,调用script_signature_verify检查客户端所传来的签名是否存在于签名文件中
    if (!script_signature_verify(qtun->lua, login->signature)) { // 签名检查失败
        msg_t* new_msg;
        unsigned char signature[sizeof(login->signature)];
        memset(signature, 0, sizeof(signature));
        SYSLOG(LOG_ERR, "unknown signature");
        pool_room_free(&qtun->pool, room_id);
        data = NULL;
        new_msg = new_login_msg(0, 0, 0, 0, 0, signature);
        if (new_msg) {
            write_c(client, new_msg, sizeof(msg_t) + msg_data_length(new_msg));
            pool_room_free(&qtun->pool, MSG_ROOM_IDX);
        }
        close_client(for_del, idx);
        goto end;
    }
    
  5. 在connect_server函数中,服务器返回数据时调用script_signature_verify接口进行校验
    if (!script_signature_verify(qtun->lua, login->signature)) {
        SYSLOG(LOG_ERR, "invalid signature");
        pool_room_free(&qtun->pool, room_id);
        goto end;
    }
    

四、完整代码

完整代码可到step15中查看

版本号:1.0.0

日期:2015-05-20

时间: 2024-10-15 01:30:39

Jump The Great Firewall【step15 支持DHCP】的相关文章

Jump The Great Firewall【step13 支持Windows】

一.解决驱动问题 由于Windows下并没有tun/tap驱动,应此对Windows版本的移植变得十分困难,不过幸运的是OpenVPN有一个开源项目<tap-windows>.借助于它的源代码,博主编写了qtun自己的驱动,姑且命名为<qtun_sys>.其主要在NDIS框架之上构建了一张虚拟网卡,借助这张虚拟网卡我们可以像Linux下的tun驱动那样来使用它.不过,令人不爽的是Windows下的驱动并不符合POSIX标准,我们不能像Linux底下那样使用open来打开它,同样也不

Jump The Great Firewall【step14 嵌入lua】

首先这里需要宣传一下,qtun的网站已经开张了,功能正在逐步添加中. 一.嵌入lua的原因 由于qtun的可配置参数不断增多,因此有必要将参数写入到配置文件之中.又由于C语言并不擅长做字符串的处理,因此加入了轻量级的lua脚本语言,同时嵌入lua更有助于加入第三方插件的支持. 二.代码的修改 包装初始化函数 extern int init_path(char* cmd); extern int init_lua(); extern void show_logo(); extern void co

Jump The Great Firewall【step16 优化丢包率】

一.为何会丢包 经过博主长期使用的经验所得,使用UDP协议在互联网上传输数据时,存在一定的丢包率.尤其是在晚间繁忙时段,丢包率更为明显,那么我们如何进行优化来降低丢包率呢? 二.如何解决 服务器端与客户端同时进行校验,在一定时间内如果发现有丢包时,发送一条新的消息通知对方重新发送该条消息 服务器端与客户端在发送消息时,每次都将一条消息发送多次 我们先来看下第一种方案:这种方案属于最传统的方式,不过该方法有一个缺点.当网络状况非常差时,发出去的请求重发消息也可能会丢失,因此对端可能会再次请求丢失的

【翻译自mos文章】Oracle Audit Vault 和Database Firewall 12.x 的平台支持情况

Oracle Audit Vault 和Database Firewall 12.x 的平台支持情况 来源于: Oracle Audit Vault and Database Firewall 12.x Platform Support (文档 ID 1536380.1) 适用于: Oracle Audit Vault and Database Firewall - Version 12.1.0.0 and later Information in this document applies t

DHCP服务过度方案(linux+dhcpd+failover)

一  现状分析 随着公司家属区接入用户的数量不断增加,以及公司网络的结构不断的复杂,静态ip地址的管理方式显得极不协调.不但配置麻烦管理不便也会造成额外的工作负担,因此对家属区提供dhcp服务十分必要. 经过对公司网络环境的研究以及现有资源的整合发现在不增加设备的前提下能够对用户提供有限的dhcp服务,但这会为将来在整个企业网中实现dhcp动态分配地址积累丰富的经验.可以先在家属区进行试点在逐步推广到全公司家属区乃至办公区. 二  网络设计与配置计划 经过对dhcp工做原理的研究发现dhcp r

小型公司网络构建——单臂路由与DHCP的结合

今天给大家带来一个小型网络构建实验,主要是思科设备单臂路由和DHCP的结合运用. 知识点: 一.单臂路由 &单臂路由 的定义及作用 单臂路由(router-on-a-stick)是指在路由器的一个接口上通过配置子接口(或"逻辑接口",并不存在真正物理接口)的方式,实现原来相互隔离的不同VLAN(虚拟局域网)之间的互联互通. &单臂路由的子接口 路由器的物理接口可以被划分为成多个逻辑接口,这些被划分后的逻辑接口被形象的称为子接口.值得注意的是这些逻辑子接口不能被单独的开启

DHCP VRRP

DHCP:dynamic host config protocol[动态主机配置协议]作用:为大量的终端主机,自动分配IP地址实现Ip地址的集中管理避免人为的配置错误 工作原理:设备角色DHCP服务器:Router,server,switch,防火墙DHCP客户端:通常情况下有上网需求的设备,都支持DHCP即都可以是DHCP客户端DHCP 中继 :网关设备 DHCP报文1 discover:发现报文:客户发送给服务器,广播2 offer:提供报文:服务器发送给客户,广播3 request:请求报

TCP/IP 协议——DHCP与自动配置详解

一个系统能够在Internet上运行并提供常用服务的基本要素: IP地址 子网掩码 DNS服务器IP地址 默认路由器IP地址 DHCP介绍 DHCP,动态主机配置协议,前身是BOOTP协议,是一个局域网的网络协议,使用UDP协议工作,常用的2个端口:67(DHCP server),68(DHCP client).DHCP通常被用于局域网环境,主要作用是集中的管理.分配IP地址,使client动态的获得IP地址.Gateway地址.DNS服务器地址等信息,并能够提升地址的使用率. DHCP有两个主

DHCP 简介

动态主机配置协议(DHCP)是一种网络管理协议,用于为进入网络的任何新节点动态分配 IP 地址.DHCP 允许自动配置节点,从而避免了网络管理员参与的必要性. DHCP 执行以下操作: 管理从网络添加或删除的所有节点的提供使用 DHCP 服务器维护主机的唯一 IP 地址只要配置为使用 DHCP 的客户端/节点连接到网络,就会向 DHCP 服务器发送请求.服务器通过向客户端/节点提供 IP 地址来确认.动态主机配置协议也称为 RFC 2131. DHCP 是一种自动方法,通过该方法可以立即为网络中