Browsed by
分类:未分类

linux路由表

linux路由表

详细的路由内容可以参考这个网站:http://linux-ip.net/

基于下面的一个事实:多个网卡的情况可以添加多个路由表
参考:

How to Add Multiple Routes in Linux Using ip Command Examples


https://www.thomas-krenn.com/en/wiki/Two_Default_Gateways_on_One_System

路由表映射文件:
/etc/iproute2/rt_tables

这个文件是路由表的映射文件,一个id:name对,表示一个路由表
#
# reserved values
#
255 local
254 main
253 default
0 unspec
#
# local
#
#1 inr.ruhep

其中内核使用的是main表。

route 命令就是查看内核路由表的main表

怎么增加一个新的路由表?
直接在上面的文件里面加入一行就可以了,比如:
1 testone
这样就加入了一个新的路由表,id为1,名字为testone

查看这个testone里面的路由规则:
ip route show table testone

在这个表里面加入规则:
加入的规则和正常加入规则一样,只是在后面要跟table testone参数:

ip route add default via 10.17.1.4 dev eth1 table testone

怎样删除增加的这个规则?
ip route del default table testone

有了table怎么才能让数据路由的时候使用这个新加的table?
用ip rule add 命令:

ip rule add from 192.168.0.4/24 table testone

上面的命令表示src为192.168.0.4的包用testone来投递

对应的删除rule:
ip rule del from 192.168.0.4

跟新路由缓存:
ip route flush cache
通过上面的步骤,一个可以工作的路由表应该就添加成功了。

有时候添加的路由表或者规则却没有按照你想要的方式进行匹配,当然我们也得有对应的调试方法。

通过下面的命令可以查看路由表的选择,匹配从优先级从小到大,
[root@test user]# ip rule show
0: from all lookup local
32765: from 192.168.0.4/24 lookup testone
32766: from all lookup main
32767: from all lookup default

怎么查看一个特殊的请求选择了哪一个路由规则?
用ip route get
ip -s route get 192.168.0.3/24 from 192.168.0.4 iis eth0

这个测试就是假设有一个数据包从网卡eth0进入,src:192.168.0.4 to:192.168.0.3/24,会选择哪一条路由?

命令ip route show cache也是一个非常重要的了解路由匹配的手段。

[root@test ~]# ip route show cache 192.168.1.4
local 192.168.1.4 from 192.168.1.2 dev lo src 192.168.1.4
cache iif peth1
从这个cache可以看出,这个路由是一个外面进来,目标为本机的数据。

[root@test ~]# ip route show cache 10.17.1.193
local 10.17.1.193 from 10.17.1.237 dev lo src 10.17.1.193
cache iif peth1
这个可以看出这个路由是local内部的路由

[root@test bin]# ip route show cache 192.168.1.4
192.168.1.4 from 10.17.1.237 dev em3 src 10.17.1.193
cache mtu 1500 advmss 1460 hoplimit 64 iif meth2
这个路由就是说目标192.168.1.4不是一个local的ip地址,也就是不在当前机器上,但是通过一条静态路由从meth2口转发出去了

这里面少了网络的拓扑结构,不太好理解,权当自己的笔记了,后面有时间优化一下,这部分知识还是比较有意思。

iptables自定义chain

iptables自定义chain

iptables应该可以算是一个脚本语言?基本概念比较简单,应用其实还是比较复杂。

基本词汇

chain:指这个规则发生的位置或者说时机。这个应该是内核钩子决定了的,没法改变。
  1. PREROUTING (路由前)
  2. INPUT (数据包流入口)
  3. FORWARD (转发管卡)
  4. OUTPUT(数据包出口)
  5. POSTROUTING(路由后)

table: 指这个规则的类型,对数据包做怎么样的操作?

  1. filter 定义允许或者不允许的
  2. nat 定义地址转换的
  3. mangle功能:修改报文原数据

table与chain的关系

不是任何位置都可以对包做任何操作的,table与chain有一些组合关系。
对于filter来讲一般只能做在3个链上:INPUT ,FORWARD ,OUTPUT
对于nat来讲一般也只能做在3个链上:PREROUTING ,OUTPUT ,POSTROUTING
而mangle则是5个链都可以做:PREROUTING,INPUT,FORWARD,OUTPUT,POSTROUTING

下面是一个经典的图:

 

自定义chain

参考:
https://www.safaribooksonline.com/library/view/linux-server-hacks/0596004613/ch04s06.html

目的:方便管理我们自己添加的iptables规则而不影响原来的规则。添加和删除

创建一个chain:
>iptables -N name_of_chain

要删除chain有两步:

1先删除chain上的规则
>iptables -F name_of_chain

2删除chain
>iptables -X name_of_chain

既然是自定义chain,这个chain一定就要属于某个位置。

假设我们需要加两条自己定义chain,一条接入PREROUTING ,另一条接入POSTROUTING
暂时定义为:
PREROUTING_TEST
POSTROUTING_TEST怎么把自定义的chain加入到对应的位置?答案是从系统自带的chain进行跳转,处理完之后再跳转回来, 用-j参数。

iptables -t nat -A PREROUTING -j PREROUTING_TEST  #把PREROUTING_VIP链接到PREROUTING后面
iptables -t nat -A PREROUTING_TEST …#增加规则到自定义chain
iptables -A PREROUTING_TEST -j RETURN  #从自定义链退出到上一层链。这里就是PREROUTING同样的方法把POSTROUTING_TEST链加入到POSTROUTING后面

怎样删除一条规则

一般有两种方法,
第一个是通过规则的内容,在前面加-D进行删除。
iptables -D INPUT -s 127.0.0.1 -p tcp –dport 111 -j ACCEPT
第二种是基于chain和这条规则在这个chain上面的序号,序号从1开始,比如删除INPUT里第四条规则:
iptables -D INPUT 4
stackoverflow的离线版本

stackoverflow的离线版本

前不久亚马逊s3宕机了几小时,再前不久gitlab也宕机了。这些服务从条款,功能的描述上来说,这样的宕机是不可能发生的!当然已经发生了,这对程序员群体的影响还是挺大的。本来我是很支持互联网是很脆弱的这一说法的,这些事件,当然不止这里提到的两个,不断的印证这一观点。然而我们却很难在短时间能有很好的办法应对。

很早以前我就在朋友圈里面调侃兜售S.O的离线版本,当然这肯定是开玩笑的。没有尝试过写爬虫去抓取内容,如果没有撒反爬虫机制的话,应该也不困难,就是很难想象我要用大的硬盘来存储这些数据。

之所以有来个真的离线版我其实也是有我的思考的。我一般到达S.O都是首先通过google的,google擅长什么啊?搜索,那么我如果光有个离线数据,没有google的搜索能力,有什么用?

等我空了研究一下,看看自己搞一个搜索的难度,如果能够达到80% google的效果,我觉得应该还是可以使用的一个程度。

关于坚持

关于坚持

坚持,这是一个说起来简单但是做起来很难的动作。坚持的对立面就是放弃,而放弃操作起来很简单!连小孩子都喜欢做简单的事情,依赖理智分析的成年人当然也不例外。

但是,往往能不能坚持做一个事情却决定了最后的结局!一个调侃性的话语是:再牛逼的梦想都经不起傻瓜的坚持。做一个翻译就是:只要你坚持,就会做成事情!

我不是一个善于坚持的人,我归结为毅力不够,执行力不够,也持续的在探索这背后的原因以及改进办法!但总不能找到,其实我也没有在寻找问题/答案的路上坚持下去,找找,找不到就放弃了!谁能坚持去找这样的东西呢?每天其他的各种事情都没办法做完!

我其中的一个探索是分析我最喜欢的是干什么,然后我就持续的去做这件事情!我知道很多时候我喜欢看美剧,美国电影。什么24小时啊,CSI啊,Walking Dead之类的,这些玩意花了我很多的时间,但是我回顾的时候却发现这些东西没给我带来什么价值,唯一的可能多了些谈资。这样我觉我应该是真的喜欢这件事:看美剧/电影。于是我有了一个计划,我得要坚持每天看至少一集美剧或者一部电影,然后写个影评!这个计划应该是还没有开始就结束了,不知道为什么,可能看剧其实不是我的真爱!

前不久朋友圈里面看到一个推荐,哈佛幸福课,说是能够治疗我这种情况,有病就得治啊!不管怎么说下面这个课程还是非常值得一看。

http://v.163.com/special/positivepsychology/

基于对这个课程的学习,我还是找到了一些新的方向,用一个词概括:routine

我首先是觉得这个理论还是很正确的,目前我还在探索如何建立routine.

再用回wordpress

再用回wordpress

都说程序员该多写代码,也该多写博客。我们不该拘泥于什么语言,不该拘泥于什么IDE,这也应该能够应用到我们写博客的工具选择上面。

但确实各种工具都有其优缺点。就像在console下面vim会优于eclipse,桌面上我也喜欢eclipse而不是vim。就像写一个快速概念验证我偏向于喜欢用python而不是C++,但是有些场景也只能用C++。这样的一些场景工具映射我有我的偏好。但是对于写博客的工具我却一直没有撒感觉。

最开始我应该是在hi.baidu.com上面写了很多C++学习的博客,但是写着写着工作忙就没写了,一不注意baidu已经把我的博客给删了,申诉也找不回来。除了百度滥发广告,我想这是我最恨百度的一次了。

hi.baidu.com之后我应该研究过很多blog平台,比如cnblog,csdn。我不太喜欢限制太多,或者出现一些平台相关的我却不能控制的内容,所以我还是不太喜欢博客平台,即使在这个平台上可以有一些流量优势,当然我写博客是为了自娱自乐,流量高低就无所谓了。就这么一直游离着,很多内容应该就写入了各种笔记软件。

我记得我中间是自己搭建过wordpress写博客的,但也不是很确切。

后面随着python使用的深入,认识了sphinx/reStrutruedText, 一下就被这个所想即所得的方式所吸引,我当时都完全不知道markdown的存在, rst在python领域里面还是有统治地位的,感觉不错。所以前后用sphinx,tinkker,  pelican做过一些静态博客的尝试。这一尝试就是几年时间过去了,唯一让我不用担心的就是维护,源代码/主题在手,github pages/vps你们挂你们的,我分分钟生成一个新的博客。 但其实我没有感觉到书写的开心,这个写作,build, 预览,修改,发布的流程让我很不开心,即使后面非常自动化了,我依然感觉这个流程有问题,但是也说不上来,就是这个流程会阻碍我开始一次写作。

这又是新的一次尝试,我“又”装回wp了,我像选择面对或者无视垃圾评论,服务器维护,数据备份,再试试所见即所得的博客形式,看看这到底会是怎么一个体验!

到今天这个博客已经运行有一段时间了,感觉不错!

转换pdf为txt

转换pdf为txt

需求是这样的:

我有一大堆api文档,都是pdf格式的,而这些pdf文档的命名又不是那么可读,我就希望能够通过文本搜索(Double Commander)找到对应的pdf文档进行阅读

很早就知道这个pattern库:http://www.clips.ua.ac.be/pattern

也玩过一些其中的api,最近算是找到正确的姿势。当然这样的用法肯定是杀鸡用了牛刀!

import os
from pattern.web import PDF
for root, directories, filenames in os.walk('.'):
	for filename in filenames: 
		file = os.path.join(root,filename)
		if file.endswith("pdf"):
			pdf = PDF(file)
			with open(file+".txt", "w") as f:
				f.write(pdf.string.encode("utf-8"))

这样就可以开心的搜索了,搜到txt就可以找到旁边的pdf了。

基于apache和imagemagic给图片加水印

基于apache和imagemagic给图片加水印

安装测试imagemagic

系统基于Ubuntu 14.04, 首先安装imagemagic

sudo apt-get install imagemagic

可以找点图片来测试一下imagemagic加水印的效果先。

composite -gravity SouthEast watermark.png input.jpg output.jpg

gravity用来设置水印图片在输入图片的位置,有下面的值可以选:
{NorthWest, North, NorthEast, West, Center, East, SouthWest, South, SouthEast}

比如Center就会把水印图片放到原始图片中间,其他的参数试一试就知道效果了。

可以命令行参数-watermark改变水印图片的透明度.geometry是个原图的百分比
-watermark geometry percent brightness and saturation of a watermark

composite -gravity SouthEast -watermark 20 watermark.png input.jpg output.jpg

配置apache扩展

如果效果已经验证了,就可以通过配置composite的处理命令给apache,让apache来自动运行这个过程。

这就需要安装模块:ext_filter,可能有多种方式进行安装,比如最原始的就是直接手动做连接,但推荐a2enmod

sudo a2enmod ext_filter

模块就安装好了,增加一个配置文件,include进主配置

    
ExtFilterDefine watermark mode=output intype=image/jpeg cmd="/usr/bin/composite -gravity Center watermark.png - -"
SetOutputFilter watermark

重启一下服务器,这样一个简易全站的图片水印功能就出来了。

细节控制

经常的,不是所有的页面上的图片都需要水印,那用apache扩展的方式能不能做到很细节的控制呢?

答案是可以的。

SetEnvIf Referer "xxx\.com/index" addwatermark
ExtFilterDefine watermark mode=output intype=image/jpeg cmd="/usr/bin/composite -gravity SouthEast /path/to/watermark.png - -" enableenv=addwatermark
SetOutputFilter watermark

以上的配置就是一个通过检查Referer来定义是否添加水印的例子。

大致的加水印流程就是

  1. 浏览器打开页面index, 这个页面加载图片/static/images/test.jpg
  2.  一个请求进来,SetEnvIf发现有对应的Referer:xxx.com/index, 设置置一个apache内部的全局环境变量addwatermark
  3. 在输出图片的时候,应用输出过滤watermark
  4. 由于这个过滤器有标志:enableenv=addwatermark,所以这个过滤器会生效,对输出图片做加水印动作。

else的流程也很明显,如果没有对应的Referer,在应用输出过滤的时候由于enableenv没有设置,等于过滤器是空的,撒都不做。

结论

由于这个水印是运行时合成,对内存和cpu的消耗应该还是很明显的。

对小流量的网站,用这种方式我觉得还是可以选择,这样可以做到不改变程序代码,而且也达到能相对细节控制的效果。

这个方式可以把水印展现给几乎所有的普通用户,一部分专业选手就没办法了,REferer这样的检查是很容易绕过的。

rust 网路编程 3 chat server

rust 网路编程 3 chat server

基于:rustc 1.0.0-beta.4 (850151a75 2015-04-30) (built 2015-05-01)

这个例子没有任何设计元素在里面,就是一个简单的概念验证。 写这个程序还是发现对rust的理解还非常浅啊。

有个奇特的结论:

rust代码写起来累,和rustc搏斗,但是回顾起来觉得很自然。 比如为什么要Arc,为什么需要mut,为什么又不需要mut

另外一个新感觉rust api不太好查,左边没有索引,有些解决方案还不如直接google问题来的快, 比如下面代码里面的从vec里面删除一个元素

后面要改进一下unwrap的使用,总是unwrap满天飞。。。, 突然有点怀念exception了,怎么办?

rust里面推荐的线程间数据传递的两种方法:

1,共享状态,Arc + Mutex

2,channel

本来是想试一试channel的,就写下面的代码写累了,就暂时放下了,后面再看有时间重构不,比如起个线程推送消息

贴个代码,自己记录一下:

use std::net::{TcpListener, TcpStream};
use std::thread;
use std::io::{Read, Write};
use std::sync::{Arc, Mutex};

fn handle_client(mut tcp_stream: TcpStream, clients: Arc< Mutex< Vec > >){
    let peer_addr = tcp_stream.peer_addr().unwrap();
    println!("{} online!", peer_addr);
    //通知其他客户端上线了
    //用了一个scope,也可以在这个scope的结尾直接drop这个MutexGuard来解锁mutex
    {
        //lock出来一个可变的Vec,下面要push进去当前这个client
        let mut other_clients = clients.lock().unwrap();
        for mut each_client in other_clients.iter(){
                each_client.write(
                        format!("{} is online!\n", peer_addr).as_bytes())
                    .unwrap();
        }
        other_clients.push(tcp_stream.try_clone().unwrap());
    }
    // 预分配一个u8数组来存储client发来的消息
    let mut message = [0; 1024];

    // 开始循环处理当前客户端的数据
    loop {
        let message_length = tcp_stream.read(&mut message).unwrap();
        if message_length == 0{
            println!("{} offline!\n", peer_addr);
            break;
        }
        print!("{} say: ", peer_addr);
        for x in 0..message_length{
            print!("{}", message[x] as char);
        }
        // 把从当前客户端读取的数据推送到所有其他的客户端
        let all_clients = clients.lock().unwrap();
        for mut each_client in all_clients.iter(){
            if each_client.peer_addr().unwrap() != tcp_stream.peer_addr().unwrap(){
                each_client.write(
                        format!("\n{} say:\n\n    ", peer_addr).as_bytes())
                    .unwrap();
                each_client.write(&message[0..message_length]).unwrap();
            }
        }
    }

    //loop跳出来到这里应该是当前这个连接已经断了,
    //从连接列表里面去掉它,
    //并且通知其他客户端下线了

    //这里allclients要mut,因为下面有remove操作
    let mut all_clients = clients.lock().unwrap();
    //下面这个实际上是个find操作,感觉vec的查找很弱啊,还得借助iter,不太像c++的stl
    let index = all_clients.iter().position(|c|{
            return c.peer_addr().unwrap() == peer_addr
        }).unwrap();
    all_clients.remove(index);
    for mut each_client in all_clients.iter(){
            each_client.write(format!("{} is offline!\n", peer_addr).as_bytes()).unwrap();
    }
}

fn main() {
    let listener = TcpListener::bind("127.0.0.1:7777").unwrap();
    //在这里定义一个全局的连接数组,方便在各个线程里面共享。
    let clients = Arc::new(Mutex::new(Vec::new()));
    for tcp_stream in listener.incoming() {
        let clients = clients.clone();
        match tcp_stream {
            Ok(tcp_stream) => {
                thread::spawn(move || {
                    handle_client(tcp_stream, clients);
                });
            }

            Err(e) => {
                println!("Fucked by the network problem: {} ", e);
            }
        }
    }
}
Rust 网路编程 2 echo server

Rust 网路编程 2 echo server

这次尝试离之前又过去了三个月了,rust已经进入了beta了:

rustc 1.0.0-beta.4 (850151a75 2015-04-30) (built 2015-05-01)

不光是网路部分,整个rust较之前都有了不小的变化。

在第一部分里面(代码在最新的rust是肯定不能用的了),我感受了一下rust的基本网络编程。但什么事情都还没有做。 这个第二部分就是来完成一个echo server.

我还是依据一个rfc来:http://tools.ietf.org/html/rfc862 这里就只实现tcp部分。 直接贴代码了,算是个笔记

use std::net::{TcpListener, TcpStream};
use std::thread;
use std::io::{Read, Write};

// 单个连接的处理函数
fn handle_client(mut stream: TcpStream){
    let peer_addr = stream.peer_addr().unwrap();
    println!("connected from: {} ", peer_addr);
    let mut data = [0; 100];
    loop {
        let len = stream.read(&mut data).unwrap();

        if len == 0{
            println!("Peer shutdown!");
            break;
        }
        print!("{} say: ", peer_addr);
        for x in 0..len{
            print!("{}", data[x] as char);
        }
        // echo to client
        let _ = stream.write(&data[0..len]).unwrap();
    }
}

fn main() {
    //RFC 说echo server要运行在端口7,这里就运行在7777吧,万恶的unwrap!
    let listener = TcpListener::bind("127.0.0.1:7777").unwrap();
    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                //这里对每一个accept的链接,启动一个线程来处理
                //线程函数是个colsure,不带参数
                //捕获上下文里面的stream变量,强制move
                thread::spawn(move || {
                    handle_client(stream);
                });
            }

            Err(e) => {
                println!("Fucked by the network problem: {} ", e);
            }
        }
    }
}

很多错误没有处理,都直接unwrap了。

下一个试试写一个聊天室程序吧。用一个非常简单需求:

1,服务器启动

2,客户端连接,发送消息到服务器

3,服务器广播消息到所有连接的客户端

Rust 网路编程 1

Rust 网路编程 1

Start

虽然现在rust的网络部分还很初级,基本都处在unstable状态。 不过简单的试试网络编程还是没问题。

最简单标准的tcp server流程:

bind

listen

accept

这样就可以开始接受外部链接。

看看rust里面是这些步骤是该怎么做?

bind

通过TcpListener结构来进行bind和listen。

在impl TcpListener下面有

fn bind(addr: A) -> IoResult

从上面的函数的定义可以看出,bind是TcpListener的static方法,通过::调用就可以了。

为什么是static方法?没有&self参数在第一个参数位置呗,很简单。

同时上面这个接口也是个很典型的泛型接口。

参数:addr 类型 A,要求A必须要实现ToSocketAddr traits

返回值:IoResult;

rust里面的惯用法就是用这种result集合进行返回。 对于这个结果,经常的如果这个结果不需要进行错误处理,就直接把结果unwrap出来,出错就结束运行。

//为了简单起见,引入io里面的所有东西.
use std::io::*;

fn main(){
     let listener = io::TcpListener::bind("127.0.0.1:9999").unwrap();
}

这个程序是没有问题的,我们bind到一个地址,用unwrap进结果判断,如果成功,就把真正的listener绑定到左边变量。

值得注意的是bind的参数是一个字符串常量&str,文档中可以看到&str是实现了ToSocketAddr这个traits的。 开始我都觉得难以置信。

下面进行

listen

impl Listener<TcpStream, TcpAcceptor> for TcpListener
fn listen(self) -> IoResult

从上面的接口,我们可以调用TcpListener的listen成员方法来得到一个IoResult<TcpAcceptor> 从这个接口来看,这里是一个值语义的self,如果TcpListener没有实现Copy的traits,这个地方就只能是move了。 也就是说,TcpListener在调用listen的时候,会把自己“消耗”掉,下面的代码都不能再使用了。

use std::io::*;
fn main(){
    let listener = io::TcpListener::bind("127.0.0.1:9999").unwrap();
    let acceptor = listener.listen().unwrap();
}

当我们有了一个acceptor的时候,我们看看怎讲来accept一个链接?

accept

impl Acceptor for TcpAcceptor
fn accept(&mut self) -> IoResult

上面的接口可以看出,可以直接调用TcpAcceptor的成员方法accept。但是注意这里的self是一个mut的引用。 所以我们要让acceptor不光是默认的只读

use std::io::*;
fn main(){
    let listener = io::TcpListener::bind("127.0.0.1:9999").unwrap();
    let mut acceptor = listener.listen().unwrap();
    let tcpstream = acceptor.accept().unwrap();
}

有了上面的代码,我就可以accept一个链接进来了。

为了看看效果,我们看看Tcpstream的文档,打印一些链接信息出来,最简单的就是看看对端的ip,端口。

下面这个函数看起来就不错

fn peer_name(&mut self) -> IoResult

其实从Tcpstream的接口来看,很多也需要可以能需要改变Tcpstream的内部状态。因为参数都要求是&mut self

use std::io::*;

fn main(){
    let listener = io::TcpListener::bind("127.0.0.1:9999").unwrap();
    let mut acceptor = listener.listen().unwrap();
    let mut tcpstream = acceptor.accept().unwrap();
    let socketaddr = tcpstream.peer_name().unwrap();
    println!("connection from: {}", socketaddr);
}

上面的代码就是一个最基本接收一个连接的server了,然后写个client试试。 只有一行一行,当然我们只是看这个链接建立的情况,不传数据。

use std::io::*;

fn main() {
    let mut stream = TcpStream::connect("127.0.0.1:9999").unwrap();
}

最后,我没有任何关闭socket的操作是不是有问题?