linux终端开发环境的配置
在终端下开发linux程序一般是通过ssh连接到安装有ssh服务器的linux(这里是Ubuntu 11.04)上,ssh客户端有很多,比如SecureCRT,开发工具一般使用vim,下面我们介绍如何搭建开发环境:
Ubuntu 11.10 系统启动默认进入终端具体解决的步骤是这样的:
vim /etc/default/grub
修改GRUB_CMDLINE_LINUX_DEFAULT=”quiet splash”为:GRUB_CMDLINE_LINUX_DEFAULT=” text”
然后运行下 update-grub2 命令更新 GRUB 的配置就搞定了.
Windows使用ssh2远程登陆UbuntuUbuntu默认没有安装SSH ,可以在新得利软件安装程序里,搜索SSH,标记并安装;或者使用命令:
apt-get install openssh-server
/etc/init.d/ssh restart
在Windows环境下使用SecureCRT选择ssh方式登陆Ubuntu
PS:注意连接编码不能选择default,应该选择utf-8,到现在可以用命令行登陆ubuntu了.
SecureCRT下vim语法高亮修改secureCRT的属性:Options->SessionOptions ->Emulation,然后把Terminal类型改成xterm,并点中ANSI Color复选框.
在/etc/profile中加入:export TERM=xterm-color.如果自己不能修改服务器上的文件,可以在自己的用户目录先新建一个.profile文件.
配置.vimrc文件:加入syntax on.
在Ubuntu中vim的配置文件存放在/etc/vim目录中,配置文件名为vimrc.如果自己不能修改服务器上的文件,可以拷贝模板到用户目录下进行修改
cp /usr/share/vim/vim72/vimrc_example.vim ~/.vimrc
- set nocompatible "去掉有关vi一致性模式,避免以前版本的bug和局限
- set nu! "显示行号
- set guifont=Luxi/ Mono/ 9 " 设置字体,字体名称和字号
- filetype on "检测文件的类型
- set history=1000 "记录历史的行数
- set background=dark "背景使用黑色
- syntax on "语法高亮度显示
- set autoindent "vim使用自动对齐,也就是把当前行的对齐格式应用到下一行(自动缩进)
- set cindent "cindent是特别针对 C语言语法自动缩进
- set smartindent "依据上面的对齐格式,智能的选择对齐方式,对于类似C语言编写上有用
- set tabstop=4 "设置tab键为4个空格,
- set shiftwidth =4 "设置当行之间交错时使用4个空格
- set ai! " 设置自动缩进
- set showmatch "设置匹配模式,类似当输入一个左括号时会匹配相应的右括号
- set guioptions-=T "去除vim的GUI版本中得toolbar
- set vb t_vb= "当vim进行编辑时,如果命令错误,会发出警报,该设置去掉警报
- set ruler "在编辑过程中,在右下角显示光标位置的状态行
- set nohls "默认情况下,寻找匹配是高亮度显示,该设置关闭高亮显示
- set incsearch "在程序中查询一单词,自动匹配单词的位置;如查询desk单词,当输到/d时,会自动找到第一个d开头的单词,当输入到/de时,会自动找到第一个以ds开头的单词,以此类推,进行查找;当找到要匹配的单词时,别忘记回车
- set backspace=2 " 设置退格键可用
- 修改一个文件后,自动进行备份,备份的文件名为原文件名加“~”后缀
if has("vms")
set nobackup
else
set backup
endif
- 如果设置完成后,发现功能没有起作用,检查一下系统下是否安装了vim-enhanced包,查询命令为:rpm -q vim-enhanced.
Ubuntu 11.10
首先,我们看一下在启用了ip_conntrack的机器上,一个数据包从进入协议栈到发出去,要经历多少查询,然后就知道如果优化掉某些次的查询了。首先需要将一个skb绑定到一个conntrack结构,这就需要一个tuple的查询,此处我们抛开流头的NAT查询以及mangle/filter rule的查询,然后进入ROUTING逻辑,首先要查询一个路由cache(幸运的是,新内核禁掉了cache查询),然后如果没有找到,则查询policy table,这样总共需要3次比较大的查询,如果路由条目很多的话,这3次查询将会非常损耗效率。
既然conntrack为每一个数据包都绑定了一个流,那么就可以将需要查询的东西在查到结果后缓存在这个流结构里面,后续的同一流的包在查询到对应的流结构时,直接取出来使用之,这样的话所有的查询就只归结到conntrack哈希的查询了,并且这种查询可以十分简单的基于硬卡来实现,大大提高了效率。本文的实验仅仅缓存路由,实际上可以缓存的东西很多,正如《我和ip_conntrack不得不说的一些事》http://blog.csdn.net/dog250/article/details/9732185最后所述,很多的策略都可以被conntrack缓存。
那么,在哪个HOOK点来缓存呢?很简单,缓存结果在POST_ROUTING的最后confirm这个地方进行(仅仅针对forward包),而查询缓存结果在PRE_ROUTING的刚刚查询到conntrack结构的地方进行。于是我就修改了ipv4_confirm和ipv4_conntrack_in这两个函数。按照标准做法,不要在nf_conn结构体中增加字段,而是使用其extend机制,遗憾的是,...系统仅仅定义了:
enum nf_ct_ext_id { NF_CT_EXT_HELPER, NF_CT_EXT_NAT, NF_CT_EXT_ACCT, NF_CT_EXT_ECACHE, NF_CT_EXT_NUM, };
这些个ID,并且写死在了nf_conntrack_extend.h中了,如果修改了就要全部重新编译,本来我想增加一个 NF_CT_EXT_ROUTE的,为了不重新编译,只是借用了NAT这个extend,实现效果即可。需要修改的文件只有一个$K/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c:
//万恶的我为了偷梁换柱,redifine了nf_conn_nat struct nf_conn_nat { struct rtable *rth; }; static struct nf_ct_ext_type route_extend __read_mostly = { .len = sizeof(struct nf_conn_nat), .align = __alignof__(struct nf_conn_nat), .id = NF_CT_EXT_NAT, .flags = NF_CT_EXT_F_PREALLOC, }; //设置conntrack的rtable static void conn_dst_set(struct nf_conn *ct, struct rtable *dst) { struct nf_conn_nat *rt = nf_ct_ext_find(ct, NF_CT_EXT_NAT); if (rt == NULL) { rt = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); if (rt == NULL) { return; } rt->rth = NULL; } #include <net/dst.h> if (rt->rth == NULL || ((rt->rth != NULL) && rt->rth->u.dst.output == dst_discard)) { dst_use(&dst->u.dst, jiffies); rt->rth = dst; } } static void save_dst(struct sk_buff *skb, struct nf_conn *ct) { struct rtable *rth; rcu_read_lock_bh(); rth = skb_rtable(skb); if (rth != NULL) { conn_dst_set(ct, rth); } rcu_read_unlock_bh(); } static unsigned int ipv4_confirm(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; const struct nf_conn_help *help; const struct nf_conntrack_helper *helper; unsigned int ret; /* This is where we call the helper: as the packet goes out. */ ct = nf_ct_get(skb, &ctinfo); //仅仅针对FORWARD包进行路由cache,因此判断HOOKNUM和sock if (ct && hooknum == NF_INET_POST_ROUTING && skb->sk == NULL && ct != &nf_conntrack_untracked) { save_dst(skb, ct); } if (!ct || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY) goto out; .... } static unsigned int ipv4_conntrack_in(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { unsigned int ret = nf_conntrack_in(dev_net(in), PF_INET, hooknum, skb); //仅仅在PRE_ROUTING检查过路包 if (ret == NF_ACCEPT && hooknum == NF_INET_PRE_ROUTING) { enum ip_conntrack_info ctinfo; struct nf_conn *ct; struct rtable *rth; struct nf_conn_nat *rt; ct = nf_ct_get(skb, &ctinfo); if (!ct) { goto out; } rcu_read_lock_bh(); rt = nf_ct_ext_find(ct, NF_CT_EXT_NAT); if (rt == NULL) { rt = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); if (rt == NULL) { rcu_read_unlock_bh(); goto out; } rt->rth = NULL; } if ((rth = rt->rth) == NULL) { rcu_read_unlock_bh(); goto out; } dst_use(&rth->u.dst, jiffies); //以下将conn的路由cache设置进skb,如此一来就不用ROUTING了 skb_dst_set(skb, dst_clone(&rth->u.dst)); rcu_read_unlock_bh(); //注意以下的被注释的代码,实际上放开这些注释的话,所实现的功能和不放开注释 //的效果是完全不同的!以下的注释可以实现HOOK点间的跳转,十分方便和硬卡进行 //接口,你可以在NF_HOOK那一行调用硬卡接口实现直接发送,然后返回NF_STOLEN // NF_HOOK(PF_INET, NF_INET_FORWARD, skb, skb->dev, rth->u.dst.dev, // rth->u.dst.input); // return NF_STOLEN; } out: return ret;; }
另外,在conntrack被free的时候,一定记得把route extend同时也free掉,否则它们就真的成为游离内存了。
正如3.x的内核将路由cache禁用的理由一样,路由cache本来就应该属于conntrack这个层次,不管是针对五元组的conntrack还是SDN那样更加广义的N元组追踪,它们本质上都是为“转发”这个动作提供一种策略,然后基于这个策略对数据包进行分类,不管是Cisco的CEF还是各种基于Netfilter的硬卡,还是SDN的用户自定义流,都是这种思想的体现,因此3.x的内核并不是说路由cache不好,而是说它应该处在它本应该属于的地方。
但是我的这个第一版修改有以下几个问题:
1.没有notify机制。也就是说如果路由改变了,要更新conntrack里面缓存的路由,或者直接失效它,这个还没有实现;
2.路由cache的timeout问题。因为conntrack中cache的路由同时也被cache到了路由缓存的list中,那么如果删除了呢?
3.没地方show出来当前都cache了哪些路由在conntrack里面
4.其实嘛,ct == &nf_conntrack_untracked也是可以cache路由的啊!
针对路由的conntrack缓存已经实现,针对ACCEPT or DROP的conntrack缓存也在我的上一版修改的基础上和IPMARK结合可以实现,想想看还有什么可以缓存的,在这一版修改后,剩下的就是设计一套硬卡的接口了,将Netfilter的HOOK实现在其中,于是这些硬卡就真正可以STOLEN软实现的数据包转发路径了。实际上,在实现上,如今的Netfilter已经很好,conntrack+IPMARK几乎可以完成所有事情,只是被DROP的流头无法创建conntrack,不过这个已经被我的第一版修改了,现在也没有问题了。在实现的建议上,建议不要增加新的HOOK点,最好用notifier_block的方式来进行事件传递。
Firefox中支持自动检测代理,其原理是访问http://wpad/wpad.pac,获取pac脚本(因此dns中必需设置wpad地址)。
因此我们的自动代理服务器并非代理服务器,而是serve wpad.pac的wpad。
pac是一个js脚本,有一套完整的标准,提高包括FindProxyForUrl 在内的很多接口函数,判断走代理还是直接访问。
以下这个实现只使用到FindProxyForUrl。它大致上就是在txt文件中加入正则规则,从而判断是否走代理。这个实现应该源自github上的autoproxy2pac。
<?php /** * This is a php implementation of autoproxy2pac */ function reg_encode($str) { $tmp_str = $str; $tmp_str = str_replace('/', "\\/", $tmp_str); $tmp_str = str_replace('.', "\\.", $tmp_str); $tmp_str = str_replace(':', "\\:", $tmp_str); $tmp_str = str_replace('%', "\\%", $tmp_str); $tmp_str = str_replace('*', ".*", $tmp_str); $tmp_str = str_replace('-', "\\-", $tmp_str); $tmp_str = str_replace('&', "\\&", $tmp_str); $tmp_str = str_replace('?', "\\?", $tmp_str); $tmp_str = str_replace('+', "\\+", $tmp_str); return $tmp_str; } function get_pac($proxy_type, $proxy_host, $proxy_port, $proxy_google) { // from 'http://autoproxy-gfwlist.googlecode.com/svn/trunk/gfwlist.txt $rulelist = file_get_contents("./gfwlist.txt") . "\n" . file_contents("./extra.txt"); $gfwlist = explode("\n", $rulelist); if ($proxy_google == "true") { $gfwlist[] = ".google.com"; } $count = 0; $pac_content = ''; $find_function_content = 'function FindProxyForURL(/blog_article/url, host/index.html) { var PROXY = "'.$proxy_type.' '.$proxy_host.':'.$proxy_port.'; DIRECT"; var DEFAULT = "DIRECT";'; foreach($gfwlist as $index=>$rule) { if (empty($rule)) continue; else if (substr($rule, 0, 1) == '!' || substr($rule, 0, 1) == '[') continue; $return_proxy = 'PROXY'; // @@开头表示默认是直接访问 if (substr($rule, 0, 2) == '@@') { $rule = substr($rule, 2); $return_proxy = "DEFAULT"; } // ||开头表示前面还有路径 if (substr($rule, 0, 2) =='||') { $rule_reg = "^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?".reg_encode(substr($rule, 2)); // !开头相当于正则表达式^ } else if (substr($rule, 0, 1) == '|') { $rule_reg = "^" . reg_encode(substr($rule, 1)); // 前后匹配的/表示精确匹配 } else if (substr($rule, 0, 1) == '/' && substr($rule, -1) == '/') { $rule_reg = substr($rule, 1, strlen($rule) - 2); } else { $rule_reg = reg_encode($rule); } // 以|结尾,替换为$结尾 if (eregi("\|$", $rule_reg)) { $rule_reg = substr($rule_reg, 0, strlen($rule_reg) - 1)."$"; } $find_function_content.='if (/' . $rule_reg . '/i.test(url)) return '.$return_proxy.';'; $count = $count + 1; } $find_function_content.='return DEFAULT;'."}"; $pac_content.=$find_function_content; $pac_content.="//".$count."\n"; $pac_content.="//version: 20121101-1730"; return $pac_content; } function main() { $proxy_type = 'PROXY'; $proxy_host = '10.102.1.1'; $proxy_port = '6543'; $proxy_google = true; $pac = get_pac($proxy_type, $proxy_host, $proxy_port, $proxy_google); header('Content-Type: application/x-ns-proxy-autoconfig'); header('Content-Length '.strlen($pac)); echo $pac; } main(); ?>
更简单的实现是,文件中直接存放正则表达式,只是这样配置性和可读性较差。这个实现一个不足的地方是全部使用正则,客户端在一个一个逐一判断js会执行得很慢。