大茶园丁 发布的文章

OpenWRT的BusyBox中小数除法并向上取整

##OpenWRT中BusyBox环境做浮点数除法并向上取整.

项目中出了点小问题,原因基本由2个方面引起:

  • 路由器中的DHCP服务器的dhcp leasetime默认12小时,而路由器地址池有限,对公共场合的路由器来说,12小时内也许很容易达到地址池用完的情况.
  • 对于免费WiFi的路由器来说,其客户端具有很强大的流动性和即时性,就是说,设备连接上后几分钟就走了.再也不回来了的.哪怕他没有达到认证时间到期. 而就算认证时间到期了.该设备离开了.而其分配的IP地址因为默认的dhcp leasetime很长而占有资源,导致新设备无法加入网络的可能.

解决办法:

  • 我们不能认为的把leasetime设置的过短,短于认证的时间,不然就会在认证时间段内,因为leasetime到期设备自动续租,自动续租到的IP地址不再相同,导致认证的IP-MAC映射关系出错而把已经认证过的设备当作新设备再次弹出Captive Portal认证.这是用户体验很差的.
  • 所以我们最好的办法就是把路由器的DHCP服务器的leasetime设置为认证时间相同的一个数值.

由于项目中认证时间用的时间单位是秒数,这个时间值是写在配置文件中的. 为了不去修改C代码,通过shell脚本去实现,就需要做OpenWrt的BusyBox环境下进行秒数转分钟数,并且应该向上取整.
一开始就想到shell里的bc命令,但是动手才发现环境有局限.折腾了一会才想到用如下的方法来做BusyBox环境下来实现这一简单需求:

auth_seconds=61.5  #比如这个是配置文件中的秒数:61.5秒

auth_minutes=`echo|awk '{printf "%.fm", ('$auth_seconds'/60)+0.5}'`;
uci set dhcp.lan.leasetime=$auth_minutes;
uci commit;
/etc/init.d/dnsmasq restart;

Shell真的是个万能胶,长期不用会生锈,要时不时磨磨刀.

Libevent2域名随机大小写DNS解析失败的解决

被evdns和国内电信厂商不遵守RFC1034中3.5节规矩的DNS服务器坑了一晚上.

一个OpenWrt MT7620路由器项目中用了libevent2的DNS异步解析,最近出现了点小BUG,经过一段时间的调试和分析,终于把问题给Kill掉了.特在此稍作总结:

在调试分析得知原因后,竟然我的问题和一个网友之前遇到的问题如出一辙,下面是转载直网友的博文 (via https://www.sunchangming.com/blog/post/4601.html) 结合我调试的实际情况稍作改动的记录:

libevent内置的异步DNS解析器采用了一种叫做DNS-0x20 bit encoding的hacking手段来防止DNS投毒攻击。但是这种做法是有问题的,在某些特殊环境(比如国内网络)下会导致DNS解析全部失败。

DNS协议对域名大小写的规定:
数据库中,域名应该是不分大小写的。

DNS协议的RFC标准是http://tools.ietf.org/html/rfc1034。在它的3.5节中特别有一段话:

“Note that while upper and lower case letters are allowed in domain names, no significance is attached to the case. That is, two names with the same spelling but different case are to be treated as if identical.”

DNS答复中,应保持域名大小写不变。DNS消息有三种类型 请求、答复、更新。请求包中域名是怎样的大小写,答复包中也应该保持一致。例如,我要查www.baidU.com,那收到的答复消息中域名也应该是www.baidU.com,而不是www.baidu.com或www.Baidu.com。而且在数据库中,www.baidU.com、www.baidu.com和www.Baidu.com应该是同一条记录。

实际上,目前有很多很多的DNS Server并不遵守这一标准。

下面是我做的实验。我在我的MacBook上用nslookup www.Baidu.com 202.106.0.20向联通的DNS(202.106.0.20)发起了请求对www.Baidu.com的查询请求,并用wireshark抓取来往的包。

请求:

0000 00 02 01 00 00 01 00 00 00 00 00 00 03 77 77 77 .............www

0010 05 42 61 69 64 75 03 63 6f 6d 00 00 01 00 01 .Baidu.com.....

答复:

0000 00 02 80 00 00 01 00 01 00 00 00 00 03 77 77 77 .............www

0010 05 62 61 69 64 75 03 63 6f 6d 00 00 01 00 01 c0 .baidu.com......

0020 0c 00 01 00 01 00 00 01 f4 00 04 77 4b d9 38 ...........wK.8

可以很明显的看出答复的包中,域名被全小写了。据我猜测,不是联通DNS Server的问题,而是中途被我的ISP搞了鬼。

什么是DNS-0x20 encoding?
它是一种未被标准化的防伪机制。DNS尽管本来就有TransactionID,但是很容易被伪造。于是就有人想,如果我们把请求包中的域名进行随机大小写,并且DNS server确实遵守rfc 1034规范,按照原样返回,那么这样就增加了中途投毒的难度。因为中途如果有人想要篡改,就得维持一个session来记录当初发过去的包是大写还是小写。ascii表中,大写字母从0x41开始,小写字母从0x61开始,恰好相差0x20。即 'A' | 0x20 = 'a'; 所以这种随机大小写的方式被称为0x20 encoding。但是我觉得,到底是你太笨还是你把别人想的太笨了?

实际上,google在构建它的public dns server(8.8.8.8)时确实也采用了这种方式。但是它深知现实与理想是不一致的,所以采用了一套白名单机制。只有位于白名单中的DNS server,才用0x20 encoding。google说这样的请求占了它70%以上的流量(“The whitelisted nameservers comprise more than 70% of our traffic.” from https://developers.google.com/speed/public-dns/docs/security)

Libevent如何实现DNS-0x20 encoding
Libevent是一套非常流行的网络库,比如Chrome、memcached就是依靠它实现非阻塞IO。在编写非阻塞IO程序时,很重要的一点是连域名解析也必须是异步的,也就是说glibc里面的那些域名解析函数都不能用。为此,libevent内置了一套异步DNS解析器,叫evdns。但是evdns的实现就要简单粗暴的很多。它默认对所有请求都采用了0x20 encoding。

evdns_base结构体内部有一个字段:


/* true iff we will use the 0x20 hack to prevent poisoning attacks. */

int global_randomize_case;

这个字段在evdns_base_new这个函数中被初始化为1,所以这个功能默认是打开的。

global_randomize_case这个开关会影响如何构造DNS request。

在evdns.c的request_new这个函数中,

if (base->global_randomize_case) {
  unsigned i;
  char randbits[(sizeof(namebuf)+7)/8];
  strlcpy(namebuf, name, sizeof(namebuf));
  evutil_secure_rng_get_bytes(randbits, (name_len+7)/8);
  for (i = 0; i < name_len; ++i) {
    if (EVUTIL_ISALPHA(namebuf[i])) {
      if ((randbits[i >> 3] & (1<<(i & 7))))
        namebuf[i] |= 0x20;
      else
        namebuf[i] &= ~0x20;
    }
  }
  name = namebuf;
}

它发现如果这个开关是打开的,就会随机的改变域名的大小写。

当从DNS Server收到reply之后,client会调用reply_parse函数来解析。下面是函数栈。

 reply_parse(evdns_base * base, unsigned char * packet, int length)

 nameserver_read(nameserver * ns)

 nameserver_ready_callback(int fd, short events, void * arg)

 event_persist_closure(event_base * base, event * ev)

 event_process_active_single_queue(event_base * base, event_list * activeq) 

 event_process_active(event_base * base)

 event_base_loop(event_base * base, int flags)

 event_base_dispatch(event_base * event_base)

int reply_parse(struct evdns_base *base, u8 *packet, int length)这个函数中,它要在请求包和答复包中按照DNS lable做查找匹配。

下面的tmp_name来自reply,cmp_name来自request。

int name_matches=0;
//...
if (base->global_randomize_case) {
if (strcmp(tmp_name, cmp_name) == 0)
  name_matches = 1;
}else {
  if (evutil_ascii_strcasecmp(tmp_name, cmp_name) == 0) 
    name_matches = 1;
}

如果全找完后name_matches是0。那么就是DNS解析失败了。

在libevent的samples目录下有一个小程序,你可以试下

# ./dns-example -v www.baidu.com

INFO: Parsing resolv.conf file /etc/resolv.conf

INFO: Added nameserver 219.239.26.42:53 as 0xbf86d0

INFO: Added nameserver 202.106.0.20:53 as 0xbf88a0

EVUTIL_AI_CANONNAME in example = 2

resolving (fwd) www.baidu.com...

INFO: Resolve requested for www.baidu.com

INFO: Setting timeout for request 0xbf8a70, sent to nameserver 0xbf86d0

INFO: Removing timeout for request 0xbf8a70

www.baidu.com: No answer (66)

不应该是No answser啊!

如果你好奇,既然chrome用的也是libevent,那么为何没这个问题呢?答案是:chrome没用libevent内置的DNS解析器,它只用了基础IO部分。

原博主的解决方法是修改libevent2的源码,关闭那个开关.然后像官方项目发Pull Request,但是可惜的是没有被合并,作者比较有原则.

其实项目作者提供给了我们灵活设置这些选项的接口API的,该API就是:

/* exported function */
int
evdns_base_set_option(struct evdns_base *base,
    const char *option, const char *val)
{
    int res;
    EVDNS_LOCK(base);
    res = evdns_base_set_option_impl(base, option, val, DNS_OPTIONS_ALL);
    EVDNS_UNLOCK(base);
    return res;
}

此方法在include/event2/dns.h中有导出:

/**
  Set the value of a configuration option.
  The currently available configuration options are:
    ndots, timeout, max-timeouts, max-inflight, attempts, randomize-case,
    bind-to, initial-probe-timeout, getaddrinfo-allow-skew.
  In versions before Libevent 2.0.3-alpha, the option name needed to end with
  a colon.
  @param base the evdns_base to which to apply this operation
  @param option the name of the configuration option to be modified
  @param val the value to be set
  @return 0 if successful, or -1 if an error occurred
 */
EVENT2_EXPORT_SYMBOL
int evdns_base_set_option(struct evdns_base *base, const char *option, const char *val);

所以,从该接口中就可以得知,一共有好几个选项是可以设置的.

我们回去看看上面方法内的实现函数evdns_base_set_option_impl的具体实现:


static int
evdns_base_set_option_impl(struct evdns_base *base,
    const char *option, const char *val, int flags)
{
    ASSERT_LOCKED(base);
    if (str_matches_option(option, "ndots:")) {
        const int ndots = strtoint(val);
        if (ndots == -1) return -1;
        if (!(flags & DNS_OPTION_SEARCH)) return 0;
        log(EVDNS_LOG_DEBUG, "Setting ndots to %d", ndots);
        if (!base->global_search_state) base->global_search_state = search_state_new();
        if (!base->global_search_state) return -1;
        base->global_search_state->ndots = ndots;
    } else if (str_matches_option(option, "timeout:")) {
        struct timeval tv;
        if (strtotimeval(val, &tv) == -1) return -1;
        if (!(flags & DNS_OPTION_MISC)) return 0;
        log(EVDNS_LOG_DEBUG, "Setting timeout to %s", val);
        memcpy(&base->global_timeout, &tv, sizeof(struct timeval));
    } else if (str_matches_option(option, "getaddrinfo-allow-skew:")) {
        struct timeval tv;
        if (strtotimeval(val, &tv) == -1) return -1;
        if (!(flags & DNS_OPTION_MISC)) return 0;
        log(EVDNS_LOG_DEBUG, "Setting getaddrinfo-allow-skew to %s",
            val);
        memcpy(&base->global_getaddrinfo_allow_skew, &tv,
            sizeof(struct timeval));
    } else if (str_matches_option(option, "max-timeouts:")) {
        const int maxtimeout = strtoint_clipped(val, 1, 255);
        if (maxtimeout == -1) return -1;
        if (!(flags & DNS_OPTION_MISC)) return 0;
        log(EVDNS_LOG_DEBUG, "Setting maximum allowed timeouts to %d",
            maxtimeout);
        base->global_max_nameserver_timeout = maxtimeout;
    } else if (str_matches_option(option, "max-inflight:")) {
        const int maxinflight = strtoint_clipped(val, 1, 65000);
        if (maxinflight == -1) return -1;
        if (!(flags & DNS_OPTION_MISC)) return 0;
        log(EVDNS_LOG_DEBUG, "Setting maximum inflight requests to %d",
            maxinflight);
        evdns_base_set_max_requests_inflight(base, maxinflight);
    } else if (str_matches_option(option, "attempts:")) {
        int retries = strtoint(val);
        if (retries == -1) return -1;
        if (retries > 255) retries = 255;
        if (!(flags & DNS_OPTION_MISC)) return 0;
        log(EVDNS_LOG_DEBUG, "Setting retries to %d", retries);
        base->global_max_retransmits = retries;
    } else if (str_matches_option(option, "randomize-case:")) {
        int randcase = strtoint(val);
        if (!(flags & DNS_OPTION_MISC)) return 0;
        base->global_randomize_case = randcase;
    } else if (str_matches_option(option, "bind-to:")) {
        /* XXX This only applies to successive nameservers, not
         * to already-configured ones.  We might want to fix that. */
        int len = sizeof(base->global_outgoing_address);
        if (!(flags & DNS_OPTION_NAMESERVERS)) return 0;
        if (evutil_parse_sockaddr_port(val,
            (struct sockaddr*)&base->global_outgoing_address, &len))
            return -1;
        base->global_outgoing_addrlen = len;
    } else if (str_matches_option(option, "initial-probe-timeout:")) {
        struct timeval tv;
        if (strtotimeval(val, &tv) == -1) return -1;
        if (tv.tv_sec > 3600)
            tv.tv_sec = 3600;
        if (!(flags & DNS_OPTION_MISC)) return 0;
        log(EVDNS_LOG_DEBUG, "Setting initial probe timeout to %s",
            val);
        memcpy(&base->global_nameserver_probe_initial_timeout, &tv,
            sizeof(tv));
    }
    return 0;
}

浏览过此段代码之后,我想心中肯定一目了然了.
那么问题的解决方法也就清楚了,只需要在evdns_base_new后加一行设置该选项为关闭就搞定. 如下:

    evdns_base_set_option(dnsbase, "randomize-case:", "0");//TurnOff DNS-0x20 encoding
    evdns_base_nameserver_ip_add(dnsbase, "180.76.76.76");//BaiduDNS
    evdns_base_nameserver_ip_add(dnsbase, "223.5.5.5");//AliDNS
    evdns_base_nameserver_ip_add(dnsbase, "223.6.6.6");//AliDNS
    evdns_base_nameserver_ip_add(dnsbase, "114.114.114.114");//114DNS
    evdns_base_nameserver_ip_add(dnsbase, "8.8.8.8");//GoogleDNS

写在最后:
开源项目就是这点方便,可以阅读其源码,有问题可以立马自己分析解决.
可惜的是开源氛围在长沙还是千里戈壁的景象.看来长沙的未来靠我戴维营哈!

【MTK】iwpriv命令使用说明

#iwpriv 命令

####1. CountryRegion 2.4GHz的国家地区码,不同的地区码信道选择范围不一样,范围是0--7,31--33
iwpriv ra0 set CountryRegion=5

####2. CountryRegionABand 5G的国家地区码

####3.CountryCode 无线国家码

####4.国家码简写标准:https://www.iso.org/obp/ui/#search
iwpriv ra0 set CountryCode=cn

####5.ChannelGeography:信道地理类型
0:Outdoor 1:Indoor 2:Both

####6.SSID 无线SSID,1~32 ASCII码
iwpriv ra0 set SSID="AAA"

####7.WirelessMode 无线模式
1.legacy 11B only;2.legacy 11A only;3.legacy 11a/b/g mixed;4.legacy 11G only;5.11ABGN mixed
6.11N only in 2.4G; 7.11GN mixed;8.11AN mixed; 9.11BGN mixed; 10.11AGN mixed;11.11N only in 5G;
14.11A/AN/AC mixed 5G band only;15. 11AN/AC mixed 5G band only.

####8.Channel 无线channel
Channel=0; 0表示自动扫描;

####9.BasicRate 无线支持的基本速率集
1.1Mbps;2.2Mbps;3.1Mbps,2Mbps;4.5.5Mbps;15.1Mbps,2Mbps,5.5Mbps,11Mbps;

####10.Beacon Period Beacon帧的周期
iwpriv ra0 set BeaconPeriod=100

####11.DtimPeriod duratin time 1~255
iwpriv ra0 set DtimPeriod=64

####12.TxPower 传输功率,0~100
iwpriv ra0 set TxPower=99

####13.DisableOLBC

####14.BGProtection 启用/禁用 无线11B or 11G保护
0:auto;1:on;2:off

####15.MaxStaNum 最大sta连接数量
0:disable 1~32

####16.TxAntenna 配置Tx天线数量
iwpriv ra0 set TxAntenna=1

####17.RxAntenna 配置Rx天线数量

####18.TxPreamble 启用/禁用Tx 前导码
iwpriv ra0 set TxPreamble=0

####19.RTSThreshold 设置RTS 阈值 1~2347

####20.FragThreshold 设置分片包阈值,256~2346
iwpriv ra0 set FragThreshold=1024

####21.TxBurst 启用/禁用Tx burst,0:disable;1:enable
iwpriv ra0 set TxBurst=1

####22.PktAggregate 启用/禁用 Tx 帧聚合,0:disable,1:enable

####23.NoForwarding 启用或禁用不同的sta的包在相同的SSID转发,0:disable;1:enable
iwpriv ra0 set NoForwarding=0

####24.NoForwardingBTNBSSID,禁用或启用在每个BSSID之间不转发0:disable;1:enable

####25.NoForwardingMBCast,禁用或启用不抓发组播/多播包

####26.HideSSID,禁用或启用隐藏SSID,0:disable;1:enable
iwpriv ra0 set HideSSID=0

####27.StationKeepAlive禁用或启用周期性自动检测活跃的sta,0:disable;1:enable
iwpriv ra0 set StationKeepAlive=1

####28.ShortSlot,禁用或启用short slot time,0:disable;1:enable
iwpriv ra0 set ShortSlot=1

####29.AutoChannelSelect,启用禁用信道自动选则,0,disable;1:旧算法,2:新算法;

####30.Debug 设置WLAN debug等级(0~5) 0:off;1:Error;2:Warning;3:Trace;4:Info;5:Loud
iwpriv ra0 set Debug=3

####31.DriverVersion 检测无线驱动版本
iwpriv ra0 set DriverVersion=0

####32.AccessPolicy 配置访问控制规则,0:允许访问AP,1:禁止访问AP
iwpriv ra0 set AccessPolicy=0

####33.ResetCounter,重设计算器
iwpriv ra0 set ResetCounter=1

####34.SiteSurvey 请求动作做站点测量
iwpriv ra0 set SiteSurvey=
被动扫描:空串,iwpriv ra0 set SiteSurvey=
主动扫描:目的SSID,iwpriv ra0 set SiteSurvey=Target_SSID

####35.CountryString 设置国家
iwpriv ra0 set CountryString=China

####36.FixedTxMode设置发送调制模式,CCK OFDM HT
iwpriv ra0 set FixedTxMode=CCK

####37.DisConnectSta断开一个指定的STA
iwpriv ra0 set DisConnectSta=00:11:22:33:44:55

####38.DisConnectAllSta 断开所有sta
iwpriv ra0 set DisConnectAllSta=1

####39.McastPhyMode 设置多播物理模式,0:Disable; 1:CCK;2:OFDM;3:HTMIX
iwpriv ra0 set McastPhyMode=0

####40.McastMcs设置多播包的MCS,0~15
iwpriv ra0 set McastMcs=0

####41.MaxStaNum 现在每一个BSS可以管理sta的最大值 1~32
iwpriv ra0 set MaxStaNum=0
0:禁用限制

####42.AutoFallBack 启用/禁用自动降低速率功能。0:disable; 1:enable
iwpriv ra0 set AutoFallBack=1

####43.MBSSWirelessMode 设置MBSS 无线物理方式
iwpriv ra0 set MBSSWirelessMode=1
0:802.11B/G mixed
1:802.11B only
2:801.11A only
4:801.11G only
6:801.11N only
7:801.11G/N mixed
8:801.11A/N mixed
9:801.11B/G/Nmixed
10:801.11A/G/N mixed
11:801.11N in 5G band only

####44.HtBw HT信道带宽设置, 0:20MHz;1:20/40 MHz
iwpriv ra0 set HtBw=1

####45.HtMcs 设置无线调制编码策略, 0~15,32:fix MCS rate, 33,自动适配
iwpriv ra0 set HtMcs=33

####46.HtGi 设置无线guard 间隔,0:长间隔;1短间隔
iwpriv ra0 set HtGi=1

####47.HtOpMode 设置HT操作模式,0:HT混合模式,1:HT greenfield模式
iwpriv ra0 set HtOpMode=0

####48.HtBaWinSize 设置Block Ack 窗口大小,1~64
iwpriv ra0 set HtBaWinSize=64

####49.HtTxBASize 设置一次传输burst中AMPDU聚合包的个数,1~64
iwpriv ra0 set HtBASize=64

####50.HtAmsdu 启用禁用A-MSDU,0 禁用,1启用
iwpriv ra0 set HtAmsdu=0

####51.HtAutoBa 启用禁用自动block ack,0 禁用,1启用
iwpriv ra0 set HtAutoBa=1

####52.HtMimoPs 启用禁用HT MIMMO power save 模式,1:enable,0:disable
iwpriv ra0 set HtMimoPs=1

####53.AP2040Rescan 触发HT20/40 coexistence重新扫描,1:触发
iwpriv ra0 set AP2040Rescan=1

####54.HtBssCoex 启用禁用HT BSS coexistence,0 禁用,1启用
iwpriv ra0 set HtBssCoex=1

####55.AssocReqRssiThres设置关联请求时接收灵敏度的阈值,使拒绝STA的关联请求在弱信号的情况下
iwpriv ra0 set AssocReqRssiThres=-88
0:关闭
0~-100RSSI的值

####56.stat 显示无线统计信息
iwpriw ra0 stat
或者:
while [ 1 ]; do iwpriv ra0 set ResetCounter=1; sleep 1; iwpriv ra0 stat; done;

####57.get_site_survey 获取扫描信息
iwpriv ra0 get_site_survey
执行该命令前先执行iwpriv ra0 set SiteSurvey=

####58.get_mac_table 获取连接到AP的sta的mac地址信息
iwpriv ra0 get_mac_talbe

####59.get_ba_table 显示BlackACK table
iwpriv ra0 get_ba_table

####60.show 显示信息
iwpriv ra0 show [parameter]
[parameter list]
1.driverinfo
2.stat
3.stainfo
4.stacountinfo
5.stasecinfo
6.bainfo
7.connStatus
8.reptinfo
9.wdsinfo
10.igmpinfo
11.mbss
12.blockch

大陆地区OS X/Unix-like系统智能路由表实现无缝翻墙.

背景吐槽

方校长搞的GFW太恼火了. 搞的我们IT从业人员上国外网站搜个google,查个资料啥的甚是不方便.
Goagent和ssh的socks5以及shadowsocks虽然可以翻出去.但是他只能工作在应用层代理.而且一旦翻出去的话..
上国外网站方便了.但是再上国内网站就奇慢无比了(当然也有GFWList自动判断丁PAC脚本)..因为我们的网络数据要先传到国外再传回国内..
然后在传到国外再传到我们的电脑上来.. 网络数据绕的累不累?反正我是累了.
所以我还是喜欢在国外的Linux机器上开个VPN服务器.然后本地通过VPN拨号出去.然后设置把所有流量都通过VPN,这样任何数据都默认先到国外去了.
这样一来..我们需要想办法搞到所有天朝各大运营商所分配的IP地址段.然后把所有ip地址段都一一设置路由表,让其走本地网关而不是ppp0接口(或者其他私有VPN接口).

稍加思索

思路大概就是吐槽的这样了.下面我们来看怎么一步一步设置.

大概步骤:

  1. 第一步:
    连接海外VPN,让所有数据默认都走VPN,就是说把vpn连接设置为默认路由.
    输入VPN服务器地址.用户名和密码. 设置通过所有VPN连接发送所有流量(Linux下设置默认路由为VPN出口)。

  2. 第二步:
    想办法获取中国所有的IP段列表,解析列表计算生成各个网段的网络地址和掩码保存到脚本当中,
    执行脚本添加路由表,国内IP走国内网关,其他的IP默认统一海外VPN出去。

立马行动

我们去哪里能得到中国的IP分配信息呢..那肯定要去管理这区域的互联网信息的机构去弄,那中国的当然就是去这地方:
亚太互联网络信息中心.
亚太互联网络信息中心(Asia-Pacific Network Information Centre,APNIC),全球五大区域性英特网注册管理机构之一,负责亚太地区IP地址、ASN(自治系统号)的分配并管理一部分根域名服务器镜像。
它提供全球性的支持互联网操作的分派和注册服务。这是成员包括网络服务提供商、全国互联网登记,和相似的组织的一个非营利,基于会员资格的组织。APNIC负责亚洲太平洋区域,包含56个经济区。

我们需要获取亚太地区IP信息表,然后过滤出中国的IP地址段,并计算各个段的网络地址和掩码,生成路由添加脚本.

下载下面这个我已经写好的自动化脚本

#!/bin/bash
#######################戴维营教育 获取中国IPv4地址段路由表 Shell脚本########
FILE=china-ip
ACTION=add
GW=`netstat -nr|grep default |tail -1|awk '{print $2}'`
rm ip_mask.txt add_route_mac.sh $FILE >/dev/null
curl  http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest -o $FILE
grep 'apnic|CN|ipv4|' $FILE | cut -f 4,5 -d'|'|sed -e 's/|/ /g' | while read ip cnt
do
mask=$(cat << EOF | bc | tail -1
pow=32;
define mylog(a)
{
        if (a<=1) return (pow);
        pow--;
        return (mylog( a/2 ));
}
mylog($cnt);
);

echo $ip/$mask/$GW/:$cnt
echo $ip/$mask >> ip_mask.txt
echo "sudo route $ACTION -net $ip/$mask $GW" >> add_route_mac.sh
chmod +x add_route_mac.sh
done

运行这个脚本之后会在当前目录下生成 add_route_mac.sh脚本文件.
然后再执行sudo add_route_mac_sh命令把上一步生成的路由项都添加的到系统的路由表里.
稍等片刻就大功告成啦.. 因为毕竟也有3700条左右的路由项.
这个时候打开国内网站自动走本地网关..国外网站自动走vpn接口.
这个时候我们来用命令测试一下到www.youtube.com和www.baidu.com的路由分别是什么样的:

其中192.168.123.1是vpn服务器那边的vpn网关..192.168.2.253是我本地无线路由的网关:

这样一来,我们就不仅仅是流量网页可以无缝翻墙了.其他一切应用都OK拉..因为我们是在网络层开刀的.而不是在应用层或传输层开刀的.

如果有朋友的公司用的软路由或者好一点的路由器..可以把这些在路由器上建立vpn客户端,然后把路由项加到路由器里.然后就整个公司都可以畅通无阻啦.让员工感觉好像不在天朝一样.

前两天才给我们的iOS开发学员讲解了TCP/IP原理, 之前有给每个人分配一个VPN账号用来google查资料. 但是估计他们觉得翻出去后打开国内网站又很慢. 总是不能两全其美. 故今天特意把这样法子简单整理个步骤. 希望能给大家带来方便..

长沙戴维营教育 iOS开发培训 Linux后台开发培训 欢迎大家一起讨论一切关于开发啊 网络啊 等等的问题.

本文最初于 2013-9-29 发表于 http://bbs.diveinedu.com/forum.php?mod=viewthread&tid=1445

Heap Sort 堆排序C语言实现

#Heap Sort-堆排序

此贴由戴维营教育学员翻译,鄙人整理,特意为戴维营教育零基础学员课外学习之用.

##堆排序 :
堆排序就是使用堆来对数组进行排序。而堆数据结构,可以被看作是一个完整的,平衡二叉树的数组对象。堆分大根堆和小根堆,小根堆(大根推)都有个属性:除了根以外的每一个节点,其值至少(至多)是该节点的父节点的值。由这个性质可知大根堆的堆顶的值肯定是所有值中最大的,小根堆的堆顶的值是所有值中最小的,因此,堆中最小的(最大的)元素被存储在根节点,而且一个节点的子树包含一些比自己较大的(较小的)节点。

##算法:
它通过插入由用户输入的元素到对应位置来构建一个堆:
1,增加堆的大小用存储插入的值.
2,将输入值插在最后的地方.
3,将插入值与其父节点的值比较,直到满足堆属性,然后将其放置在其正确位置.

1, heap[1]是最小的元素,因此,我们删除heap[1],堆的大小减小。
2,现在heap[1]必须用新值来填补,我们把最后一个元素放这地方,看看它是否适合;如果不适合,采取两子节点中的最小元素替代父节点。
3,再看看最后一个元素在那个地方是否适合。

##特性:
最好情况下执行 - 当输入数组早已排序好, 时间复杂度: O(nlogn)
最坏情况下执行 - 当输入数组是倒序, 时间复杂度: O(nlogn);
平均情况下执行 - 时间复杂度: O(nlogn);
它不需要一个额外的空间来排序,因此空间复杂度是: O(1)
它是不稳定的排序算法;

##堆排序C语言实现

#include <stdio.h>
#include <limits.h>
/*Declaring heap globally so that we do not need to pass it as an argument every time*/

/* Heap used here is Min Heap */
int heap[1000000],heapSize;

/*Initialize Heap*/
void Init()
{
        heapSize = 0;
        heap[0] = -INT_MAX;
}

/*Insert an element into the heap */
void Insert(int element)
{
        heapSize++;
        heap[heapSize] = element; /*Insert in the last place*/
        /*Adjust its position*/
        int now = heapSize;
        while(heap[now/2] > element)
        {
                heap[now] = heap[now/2];
                now /= 2;
        }
        heap[now] = element;
}

int DeleteMin()
{
        /* heap[1] is the minimum element. So we remove heap[1]. Size of the heap is decreased.Now heap[1] has to be filled. We put the last element in its place and see if it fits.If it does not fit, take minimum element among both its children and replaces parent with it.Again See if the last element fits in that place.*/
        int minElement,lastElement,child,now;
        minElement = heap[1];
        lastElement = heap[heapSize--];
        /* now refers to the index at which we are now */
        for(now = 1; now*2 <= heapSize ;now = child)
        {
                /* child is the index of the element which is minimum among both the children */
                /* Indexes of children are i*2 and i*2 + 1*/
                child = now*2;
                /*child!=heapSize beacuse heap[heapSize+1] does not exist, which means it has only one child */
                if(child != heapSize && heap[child+1] < heap[child] )
                {
                        child++;
                }
                /* To check if the last element fits ot not it suffices to check if the last element is less than the minimum element among both the children*/
                if(lastElement > heap[child])
                {
                        heap[now] = heap[child];
                }
                else /* It fits there */
                {
                        break;
                }
        }
        heap[now] = lastElement;
        return minElement;
}

int main(void)
{
        int number_of_elements;
        scanf("%d",&number_of_elements);
        int iter, element;
        Init();
        for(iter = 0;iter < number_of_elements;iter++)
        {
                scanf("%d",&element);
                Insert(element);
        }
        for(iter = 0;iter < number_of_elements;iter++)
        {
                printf("%d ",DeleteMin());
        }
        printf("\n");
        return 0;
}