Category Archives: Network

NGINX userid 分析、解码

生成userid的代码在 http/modules/ngx_http_userid_filter_module.c 大概550行左右。
uid_set 是4个uint32构成的,其中比较有用的是第二个unit32,是userid的生成时间。第四个是一个递增值 以 0x03030302 为初始值,每次递增0x100。

default: /* AF_INET */
                sin = (struct sockaddr_in *) c->local_sockaddr;
                ctx->uid_set[0] = sin->sin_addr.s_addr;
                break;
            }

        } else {
            ctx->uid_set[0] = htonl(conf->service);
        }

        ctx->uid_set[1] = htonl((uint32_t) ngx_time());
        ctx->uid_set[2] = htonl(start_value);
        ctx->uid_set[3] = htonl(sequencer_v2);
        sequencer_v2 += 0x100;
        if (sequencer_v2 < 0x03030302) {
            sequencer_v2 = 0x03030302;
        }
// 用PHP解码nginx userid
$str = $_REQUEST["uid"] ?: $_COOKIE['uid'];

function nginx_userid_decode($str)
{
        return  unpack('N*', base64_decode(str_replace(' ', '+', $str)));
}

$hash = nginx_userid_decode($str);
var_dump($hash);
date_default_timezone_set("UTC");
var_dump(date("Y-m-d H:i:s", $hash[2]));

参考文章:
http://www.lsproc.com/blog/nginx_userid_decode/

Read: 2406

[转]开发大型高负载类网站应用的几个要点

作者:nightsailer
来源:http://www.phpchina.com/bbs/thread-15484-1-1.html

看了一些人的所谓大型项目的方法,我感觉都是没有说到点子上,有点难受。
我也说说自己的看法.我个人认为,很难衡量所谓项目是否大型,
即便很简单的应用在高负载和高增长情况下都是一个挑战.因此,按照我的想法,姑且说是高负载
高并发或者高增长情况下,需要考虑的问题.这些问题,很多是和程序开发无关,而是和整个系统的
架构密切相关的.

  • 数据库

没错,首先是数据库,这是大多数应用所面临的首个SPOF。尤其是Web2.0的应用,数据库的响应是首先要解决的。
一般来说MySQL是最常用的,可能最初是一个mysql主机,当数据增加到100万以上,
那么,MySQL的效能急剧下降。常用的优化措施是M-S(主-从)方式进行同步复制,将查询和操作和分别在不同的
服务器上进行操作。我推荐的是M-M-Slaves方式,2个主Mysql,多个Slaves,需要注意的是,虽然有2个Master,
但是同时只有1个是Active,我们可以在一定时候切换。之所以用2个M,是保证M不会又成为系统的SPOF。
Slaves可以进一步负载均衡,可以结合LVS,从而将select操作适当的平衡到不同的slaves上。

以上架构可以抗衡到一定量的负载,但是随着用户进一步增加,你的用户表数据超过1千万,这时那个M变成了
SPOF。你不能任意扩充Slaves,否则复制同步的开销将直线上升,怎么办?我的方法是表分区,
从业务层面上进行分区。最简单的,以用户数据为例。根据一定的切分方式,比如id,切分到不同的数据库集群去。
全局数据库用于meta数据的查询。缺点是每次查询,会增加一次,比如你要查一个用户nightsailer,你首先要到
全局数据库群找到nightsailer对应的cluster id,然后再到指定的cluster找到nightsailer的实际数据。
每个cluster可以用m-m方式,或者m-m-slaves方式。
这是一个可以扩展的结构,随着负载的增加,你可以简单的增加新的mysql cluster进去。

需要注意的是:
1、禁用全部auto_increment的字段
2、id需要采用通用的算法集中分配
3、要具有比较好的方法来监控mysql主机的负载和服务的运行状态。如果你有30台以上的mysql数据库在跑就明白我的意思了。
4、不要使用持久性链接(不要用pconnect),相反,使用sqlrelay这种第三方的数据库链接池,或者干脆自己做,因为php4中mysql的
链接池经常出问题。

  • 缓存

缓存是另一个大问题,我一般用memcached来做缓存集群,一般来说部署10台左右就差不多(10g内存池)。需要注意一点,千万不能用使用
swap,最好关闭linux的swap。

  • 负载均衡/加速

可能上面说缓存的时候,有人第一想的是页面静态化,所谓的静态html,我认为这是常识,不属于要点了。页面的静态化随之带来的是静态服务的
负载均衡和加速。我认为Lighttped+Squid是最好的方式了。
LVS <------->lighttped====>squid(s) ====lighttpd

上面是我经常用的。注意,我没有用apache,除非特定的需求,否则我不部署apache,因为我一般用php-fastcgi配合lighttpd,
性能比apache+mod_php要强很多。

squid的使用可以解决文件的同步等等问题,但是需要注意,你要很好的监控缓存的命中率,尽可能的提高的90%以上。
squid和lighttped也有很多的话题要讨论,这里不赘述。

  • 存储

存储也是一个大问题,一种是小文件的存储,比如图片这类。另一种是大文件的存储,比如搜索引擎的索引,一般单文件都超过2g以上。
小文件的存储最简单的方法是结合lighttpd来进行分布。或者干脆使用Redhat的GFS,优点是应用透明,缺点是费用较高。我是指
你购买盘阵的问题。我的项目中,存储量是2-10Tb,我采用了分布式存储。这里要解决文件的复制和冗余。
这样每个文件有不同的冗余,这方面可以参考google的gfs的论文。
大文件的存储,可以参考nutch的方案,现在已经独立为hadoop子项目。(你可以google it)

其他:
此外,passport等也是考虑的,不过都属于比较简单的了。

吃饭了,不写了,抛砖引玉而已。

【回复】

9tmd :
说了关键的几个部分,还有一些比如squid群、LVS或者VIP(四层交换)之类的必须考虑,数据库逻辑分表不需要master里面查id,可以定期缓存或者程序逻辑上进行控制。
跟大家分享一下我的经验: http://www.toplee.com/blog/archives/337.html (欢迎讨论)
nightsailer
楼上说的很好.
我 再说一下关于为何要在主表查询,最主要的因素是考虑到复制和维护的问题。假设按照程序逻辑,用户nightsailer应该在s1集群,但是由于种种原 因,我须要将nightsailer的数据从s1集群转移到s5集群或者某些时候,我需要将某几个集群的数据合并,此时,我维护的时候只需要更新一下主数 据库中nightsailer的cluster id从1变成5,,维护的工作可以独立进行,无需考虑更新应用程序的逻辑。也许程序的id分配逻辑可以考虑到这种情况,但是这样一来,你的这个逻辑会发散 到各个应用中,产生的代码的耦合是很高的。相反,采用查表这种方式,只需要在最初的时候进行初始分配,那么其他的应用是无需考虑这些算法和逻辑的。
当然,我最初提到的增加这次查询并不是说每次查询都需要找主数据库,缓存策略是必定要考虑的。

至于说为什么要禁用auto_increment,我想也清楚了,数据的合并和分隔,肯定是不能用auto_increment的。

在闲扯一下,PHP的优化可以有很多,主要的措施:
1、使用FCGI方式,配合lighttpd,Zeus.
我个人比较喜欢Zeus,简单可靠。不过,需要¥¥¥。
lighty也不错,配置文件也很简单,比较清爽。最新的1.5,虽然不稳定,但是配合linux的aio,性能的提升
非常明显。即便现在的稳定版,使用2.6的epoll可以得到的性能是非常高。当然,lighty比zeus缺点是对于smp
的支持很有限,所以可以采用多服务器负载,或者干脆起不同的进程服务监听不同的端口。
2、专门的PHP FCGI服务器。
好处多多,在这个服务器上,就跑php的fcgi服务,你可以把一些缓存加上,比如xcache,我个人喜欢这个。
还有别的,套用大腕的话,把能装的都装上,呵呵。
另外,最主要的是,你可以只维护一个php的环境,这个环境能够被apache,zeus,lighttpd同时share,
前提是这些都使用php的fcgi模式,而且,xcache可以充分发挥!
3、apache+mod_fastcgi
apache并非无用,有时候也需要。比如我用php做了一个web_dav的服务器,在其他有问题,只能跑apache.
那么,apache安装一下mod_fastcgi,通过使用externl server,使用2配置的php fastcgi。
4、优化编译
ICC是我的首选,就是intel的编译器啦,用icc重新编译php,mysql,lighty,能编的都编,会有不小的收获的。尤其是你用
intel的cpu的话。
5、php4的64位需要patch
好像没有人在linux x86_64上编译过php4吧,我曾经googleit
,别说国内了,连老外都很少用。
这里就做个提醒把,如果用php官方下载的(包括最新的php-4.4.4),统统无法编译通过。问题是出在autoconf上,需要
手工修改config.m4,一般是在mysql,gd,ldap等一些关键的extension上,还有phpize的脚本。把/usr/lib64加入到
config.m4中相关搜索的path中。
不过我估计很少人像我这样死用php4不防,呵呵。php5就没有问题。
我也考虑正在迁移到php5.2,写代码太方便了,一直忍着呢。
QUOTE:
原帖由 wuexp 于 2007-1-3 17:01 发表
分表会使操作数据(更改,删除,查询)边的很复杂,特别是遇到排序的时候就更麻烦了.
曾经考虑根据用户id哈希一下,插入到相应的分表里

明白你的意思。

不过我们可能讨论的不完全一样,呵呵。
我所说的分表要依据不同的业务情况来划分的,

1、可以是垂直划分,
比如依据业务实体切分,比如用户a的blog贴子,用户的tag,用户的评论都在a数据库u,甚者是完整的一套数据结构(这种情况下应该说是分数据库)

2、也可以水平划分,
一个表的数据分在不同的数据库上。
比如message表,你可能分为daily_message,history_message,
dialy_meesage可能是hot对象,week_message是warm,2个月以前的帖子
可能属于cold对象了。这些对象依据访问频度不同会划分到不同的数据库群上。

3、二者结合

不过,不论如何,更改、删除并不复杂,和未分区的表没有区别。

至于查询和排序,不可能仅仅是通过select,order吧?
而是应该产生类似摘要表,索引表,参考表。。。
另外,要根据业务具体分析减少垃圾数据,有些时候,只需要最初的1万条记录,那么所有表
数据的排序就不需要了。很多传统的业务,比如零售,流水表很大,但是报表的数据
并非实时生成的,扎报表应该不陌生。

也可以参考很多网站的做法,比如technorati啊,flickr之类的。

所谓的麻烦是你设计系统的结构的时候要考虑到,在设计数据库的时候更要注意,
因此只要项目的framework最初设计比较完备,那么可以说大部分对开发人员是透明的。
前提是,你一定要设计好,而不是让程序员边写代码边设计,那会是噩梦。

我写这么多废话,并非仅仅是对程序员来说,也许对设计者更有用。

9tmd
程序逻辑上控制表拆分只需要维护一个数据库访问的配置文件即可,对于开发来说,完全透明,可以不用关心访问的是哪里,而只需要调用通用的接口即可,曾经做过的系统里面,这样的应用经常遇到,尤其在全网passport、社区帖子等方面的处理上应用最多。

原来在yahoo工作和后来mop工作都使用了这样的架构,整体感觉来说还是值得信赖的,单表毕竟存在面对极限数据量的风险。

9tmd
前 面老是有人问auto_increment的问题,其实这是MySQL官方专门针对M/S的Replication做过的说明,因为MySQL的同步是依 靠同步MySQL的SQL日志来实现的,事实上单向的Master->Slave使用auto_increment是没有问题的,而双向的M/M模 式就会存在问题了,稍微一思考就知道怎么回事了。官方文档:
http://dev.mysql.com/tech-resour ... ql-replication.html
http://dev.mysql.com/doc/refman/ ... auto-increment.html

另 外,在使用MySQL的同步时,需要注意在自己的代码里面,写SQL的时候不要使用MySQL自己提供的类似 NOW()之类的函数,而应该使用php程序里面计算的时间带入SQL语句里面,否则同步的时候也可能导致值不相等,这个道理可以牵涉出另外一些类似的问 题,大家可以考虑一下。

Read: 830

网络层访问权限控制技术-ACL详解

技术从来都是一把双刃剑,网络应用与互联网的普及在大幅提高企业的生产经营效率的同时,也带来了诸如数据的安全性,员工利用互联网做与工作不相干事等负面影响。如何将一个网络有效的管理起来,尽可能的降低网络所带来的负面影响就成了摆在网络管理员面前的一个重要课题。

A公司的某位可怜的网管目前就面临了一堆这样的问题。A公司建设了一个企业网,并通过一台路由器接入到互联网。在网络核心使用一台基于IOS的多层交换 机,所有的二层交换机也为可管理的基于IOS的交换机,在公司内部使用了VLAN技术,按照功能的不同分为了6个VLAN。分别是网络设备与网管 (VLAN1,10.1.1.0/24)、内部服务器(VLAN2)、Internet连接(VLAN3)、财务部(VLAN4)、市场部 (VLAN5)、研发部门(VLAN6),出口路由器上Fa0/0接公司内部网,通过s0/0连接到Internet。每个网段的三层设备(也就是客户机 上的缺省网关)地址都从高位向下分配,所有的其它节点地址均从低位向上分配。该网络的拓朴如下图所示:  

  自从网络建成后麻烦就一直没断过,一会儿有人试图登录网络设备要捣乱;一会儿领导又在抱怨说互联网开通后,员工 成天就知道泡网;一会儿财务的人又说研发部门的员工看了不该看的数据。这些抱怨都找这位可怜的网管,搞得他头都大了。那有什么办法能够解决这些问题呢?答 案就是使用网络层的访问限制控制技术――访问控制列表(下文简称ACL)。

那么,什么是ACL呢?ACL是种什么样的技术,它能做什么,又存在一些什么样的局限性呢?

ACL的基本原理、功能与局限性

网络中常说的ACL是Cisco IOS所提供的一种访问控制技术,初期仅在路由器上支持,近些年来已经扩展到三层交换机,部分最新的二层交换机如2950之类也开始提供ACL的支持。只 不过支持的特性不是那么完善而已。在其它厂商的路由器或多层交换机上也提供类似的技术,不过名称和配置方式都可能有细微的差别。本文所有的配置实例均基于 Cisco IOS的ACL进行编写。

基本原理:ACL使用包过滤技术,在路由器上读取第三层及第四层包头中的信息如源地址、目的地址、源端口、目的端口等,根据预先定义好的规则对包进行过滤,从而达到访问控制的目的。

功能:网络中的节点资源节点和用户节点两大类,其中资源节点提供服务或数据,用户节点访问资源节点所提供的服务与数据。ACL的主要功能就是一方面保护资源节点,阻止非法用户对资源节点的访问,另一方面限制特定的用户节点所能具备的访问权限。

配置ACL的基本原则:在实施ACL的过程中,应当遵循如下两个基本原则:

最小特权原则只给受控对象完成任务所必须的最小的权限

最靠近受控对象原则:所有的网络层访问权限控制

局限性: 由于ACL是使用包过滤技术来实现的,过滤的依据又仅仅只是第三层和第四层包头中的部分信息,这种技术具有一些固有的局限性,如无法识别到具体的人,无法 识别到应用内部的权限级别等。因此,要达到end to end的权限控制目的,需要和系统级及应用级的访问权限控制结合使用。

ACL基本配置

ACL配置技术详解

“说那么多废话做什么,赶快开始进行配置吧。”,A公司的网管说。呵呵,并不是我想说那么多废话,因为理解这些基础的概念与简单的原理对后续的配置和排错都是相当有用的。说说看,你的第一个需求是什么。

“做为一个网管,我不期望普通用户能telnet到网络设备”――ACL基础

“补充一点,要求能够从我现在的机器(研发VLAN的10.1.6.66)上telnet到网络设备上去。”。hamm,是个不错的主意,谁都不希望有 人在自己的花园中撤野。让我们分析一下,在A公司的网络中,除出口路由器外,其它所有的网络设备段的是放在Vlan1中,那个我只需要在到VLAN 1的路由器接口上配置只允许源地址为10.1.6.66的包通过,其它的包通通过滤掉。这中只管源IP地址的ACL就叫做

标准IP ACL:

我们在SWA上进行如下的配置:

access-list 1 permit host 10.1.6.66

access-list 1 deny any

int vlan 1

ip access-group 1 out

这几条命令中的相应关键字的意义如下:

access-list配置均ACL的关键字,所有的ACL均使用这个命令进行配置。

access-list后面的1:ACL号,ACL号相同的所有ACL形成一个组。在判断一个包时,使用同一组中的条目从上到下逐一进行判断,一遇到满足的条目就终止对该包的判断。1-99为标准的IP ACL号,标准IP ACL由于只读取IP包头的源地址部分,消耗资源少。

permit/deny:操作。Permit是允许通过,deny是丢弃包。

host 10.1.6.66/any匹 配条件,等同于10.1.6.66 0.0.0.0。刚才说过,标准的ACL只限制源地址。Host 10.1.6.66(10.1.6.66 0.0.0.0)的意思是只匹配源地址为10.1.6.66的包。0.0.0.0是wildcards,某位的wildcards为0表示IP地址的对应 位必须符合,为1表示IP地址的对应位不管是什么都行。简单点说,就是255.255.255.255减去子网掩码后的值,0.0.0.0的 wildcards就是意味着IP地址必须符合10.1.6.66,可以简称为host 10.1.6.66。any表示匹配所有地址。

注意:IOS中的ACL均使用wildcards,并且会用wildcards对IP地址进行严格的对齐,如你输入一条access-list 1 permit 10.1.1.129 0.0.0.31,在你show access-list看时,会变成access-list 1 permit 10.1.1.128 0.0.0.31,PIXOS中的ACL均使用subnet masks,并且不会进行对齐操作。

int vlan1///ip access-group 1 out这两句将access-list 1应用到vlan1接口的out方向。其中1是ACL号,和相应的ACL进行关联。Out是对路由器该接口上哪个方向的包进行过滤,可以有in和out两种选择。

注意:这里的in/out都是站在路由器或三层模块(以后简称R)上看的,in表示从该接口进入R的包,out表示从该接口出去的包。

好了,这就是一个最基本的ACL的配置方法。什么,你说普通用户还能telnet到RTA?那你在int vlan3上现加一个ip access-group 1 out吧。Hammmm,等等,你这样加上去普通用户就访问不了internet了。让我们把刚才的ACL去掉,重新写一个。

回忆一下,我们的目的是除了10.1.6.66能够进行telnet操作外,其它用户都不允许进行telnet操作。刚才我们说过,标准的IP ACL只能控制源IP地址,不能控制到端口。要控制到第四层的端口,就需要使用到:

扩展的IP ACL的配置

  先看看配置实例吧。在SWA上进行如下配置:

int vlan 1

no ip access-group 1 out

exit

no access-list 1

access-list 101 permit tcp host 10.1.6.66 any eq telnet

access-list 101 deny tcp any any eq telnet

int vlan 1

ip access-group 101 out

int vlan 3

ip access-group 101 out

你应该注意到到这里的ACL有一些变化了,现在对变化的部分做一些说明:

access-list 101:注 意这里的101,和刚才的标准ACL中的1一样,101是ACL号,表示这是一个扩展的IP ACL。扩展的IP ACL号范围是100-199,扩展的IP ACL可以控制源IP、目的IP、源端口、目的端口等,能实现相当精细的控制,扩展ACL不仅读取IP包头的源地址/目的地址,还要读取第四层包头中的源 端口和目的端口,的IP在没有硬件ACL加速情况下,会消耗大量的CPU资源。

int vlan 1///no ip access-group 1 out///exit///no access-list 1:取消access-list 1,对于非命名的ACL,可以只需要这一句就可以全部取消。注意,在取消或修改一个ACL前,必须先在它所应用的接口上先把应用给no掉,否则会导致相当严重的后果。

tcp host 10.1.6.66 any eq telnet:匹配条件。完整格式为:协议 源地址 源wildcards [关系] [源端口] 目的地址 目的wildcards [关系] [目的端口]。其 中协议可以是IP、TCP、UDP、EIGRP等,[]内为可选字段。仅在协议为tcp/udp等具备端口号的协议才有用。关系可以是eq(等于)、 neq(不等于)、lt(大于)、range(范围)等。端口一般为数字的1-65535,对于周知端口,如23(服务名为telnet)等可以用服务名 代替。源端口和目的端口不定义时表示所有端口。

把这个ACL应用上去后,用户们开始打电话来骂娘了,因为他们都访问不了Internet了,是哪里出了问题了呢?

注 意:所有的ACL,缺省情况下,从安全角度考虑,最后都会隐含一句deny any(标准ACL)或deny ip any any(扩展IP ACL)。所以在不了解业务会使用到哪些端口的情况下,最好在ACL的最后加上一句permit ip any any,在这里就是access-list 101 permit ip any any。

现在用户倒是能够 访问Internet了,但我们的可怜的网管却发现普通用户还是能够telnet到他的SWA上面,因为SWA上面有很多个网络接口,而且使用扩展的 ACL会消耗很多的资源。有什么简单的办法能够控制用户对网络设备的Telnet访问,而又不消耗太多的资源呢?这就需要使用到:

对网络设备自身的访问如何进行控制的技术

让我们先把刚才配置的ACL都取掉(具体配置略,不然后读者会以为我在骗稿费了。),再在每台网络设备上均进行如下配置:

access-list 1 permit host 10.1.6.66

line vty 0 4(部分设备是15)

access-class 1 in

这样就行了,telnet都是访问的设备上的line vty,在line vty下面使用access-class与ACL组进行关联,in关键字表示控制进入的连接。

就这么简单?wk,你丫是不是在玩我们,为什么还要绕一大圈?臭鸡蛋和烂西红柿开始在70的脑袋上方狂飞。(5555555,偶也只是想向大家把ACL的基础知识讲的明白一些的嘛)。经过刚才的配置,我们可以理出一个简单的ACL配置步骤了:

分析需求,找清楚需求中要保护什么或控制什么;为方便配置,最好能以表格形式列出。在本文的后面会举例的。

分析符合条件的数据流的路径,寻找一个最适合进行控制的位置;

书写ACL,并将ACL应用到接口上;

测试并修改ACL。

当A公司的领导知道在网管能够控制普通用户对网络设备的访问后,我们的可怜的网管就收到了很多看起来很难的要求。领导要求网管:

ACL执行顺序

“使用ACL技术对网络访问进行精细化控制”――ACL进阶配置

命名的IP ACL

由于最近服务器网段的机器老是被人用telnet、rsh等手段进行攻击,我们只对员工开放web服务器(10.1.2.20)所提供的http、 FTP服务器(10.1.2.22)提供的FTP服务和数据库服务器(10.1.2.21:1521)。好吧,我们着手进行配置,可是我们的ACL刚写到 一半,发现前面写的几句好像有问题,一个no命令输进去,整个ACL都没了,唉,一切都得重来,难道就没有一个变通的办法么?有,这里我就需要用到:

命名的IP acl提供的两个主要优点是:

l 解决ACL号码不足的问题。

l 可以自由的删除ACL中的一条语句,而不必删除整个ACL。

命名的ACL的主要不足之处在于无法实现在任意位置加入新的ACL条目。比如上面那个例子中,我们进行了如下的配置:

ip access-list extend server-protect

permit tcp 10.1.0.0 0.0.255.255 host 10.1.2.20 eq www

permit tcp 10.0.0.0 0.0.255.255 host 10.1.2.21 eq 1521

permit tcp 10.1.0.0 0.0.255.255 host 10.1.2.22 eq ftp

配置到这里,我们发现permit tcp 10.0.0.0 0.0.255.255 host 10.1.2.21 eq 1521这句配错了,我们得把它给取掉并重新配置,OK,我样可以简单的进行如下配置:

ip access-list extend server- protect

no permit tcp 10.0.0.0 0.0.255.255 host 10.1.2.21 eq 1521

permit tcp 10.1.0.0 0.0.0.255 host 10.1.2.21 eq 1521

exit

int vlan 2

ip access-group server- protect

就可以了。现在对命名的IP access-list的配置方法解释如下:

ip access-list extend server-access-limit:ip access-list相当于使用编号的access-list中的access-list段。extend表明是扩展的ACL(对应地, standard表示标准的ACL)。server-access-limit是access-list的名字,相当于基于编号的ACL中的编号字段。

permit tcp 10.1.6.0 0.0.0.255 host 10.1.2.21 eq 1521这一段和使用编号的access-list的后半段的意义相同,都由操作和条件两段组成。

其实基于名字的IP ACL还有一个很好的优点就是可以为每个ACL取一个有意义的名字,便于日后的管理与维护。所以Ultra工作室强烈建议各位看官在实际工作中均使用命名的ACL。

进一步完善对服务器数据的保护――ACL执行顺序再探讨

在服务器网段中的数据库服务器中存放有大量的市场信息,市场部门的人员不希望研发部门访问到数据库服务器,经过协商,同意研发部门的领导的机器(IP地址为10.1.6.33)可以访问到数据库服务器。这样,我们的服务器网段的的访问权限部分如下表所示:

协议 源地址 源端口 目的地址 目的端口 操作
TCP 10.1/16 所有 10.1.2.20/32 80 允许访问
TCP 10.1/16 所有 10.1.2.22/32 21 允许访问
TCP 10.1/16 所有 10.1.2.21/32 1521 允许访问
TCP 10.1.6/24 所有 10.1.2.21/32 1521 禁止访问
TCP 10.1.6.33/32 所有 10.1.2.21/32 1521 允许访问
IP 10.1/16 N/A 所有 N/A 禁止访问

Read: 876

PXE技术简介


     PXE(Preboot Execution Environment)是INTEL推出的可用于无盘、有盘联网的网络协议。使用PXE功 能,首先要求客户端网卡的支持,其次,在服务器端也要有支持此协议的网管软件。在远程启动过程中,客户端通过PXE(存放于主板或者网卡上的 BIOS ROM中)向服务器发生请求,要求服务器分配一个IP地址,再用TFTP(Trivial File Transfer Protocol/小 文件传输协议)或MTFTP(Multicast Trivial File Transfer Protocol)协议下载一个启动软件包到本机内存中 并执行,由这个启动软件包完成终端基本软件设置,从而引导预先安装在服务器中的终端操作系统。从启动过程可以看出,PXE不需要记录客户端的网卡型号和 MAC地址等信息,因此只要设置好服务器端就可以应用于任一台带PXE协议的终端,减少了在PRL方式下对于不同网卡要做不同设置的重复劳动。PXE可以 引导多种终端操作系统,如Window95/98或Linux等。由于PXE协议将数据分割成极为细小的部分,所以数据的传输速度大大加快,相对于以前的 RPL远程引导无盘启动技术,PXE的运行速度提高三倍以上。PXE技术除目前支持无盘Win98启动外,不久将能支持WIN2000,以及LINUX, 而现时其它方案只能支持无盘WIN95启动。并且,在PXE4.0以上的版本,加入了启动安全验证服务BIS (Boot Integrity Services),更加强了网络的安全性。

PXE启动详解

     当客户机启动后,其网卡上的Boot Rom会广播一个引导请求帧FIND,该帧中包含有客户机的网卡MAC地址,server端远程启动 服务收到客户机广播的FIND帧后,根据帧中所带的客户机网卡MAC地址,检查远程启动数据库中是否有该卡号的配置记录:如果不存在这个记录,引导过程不 能继续;如果此工作站的记录已经存在,远程启动服务发送一个FOUND帧,同样,这个帧中包含了服务器的网卡MAC地址,然后就调用BOOTP (boot protocol/启动协议)或者DHCP( Dynamic Host Configuration Protocol/动态主机配置协 议,其实是BOOTP的进化), 分配给工作站端的机器名字、IP地址、服务器端的IP地址以及启动时的映像文件等。
     工作站收到第一个响应后(工作站只对收到的第一个FOUND帧有响应),则发送一个SEND.FILE.REQUEST帧给第一个响应的远程启动服务器,以请求传送启动所需的文件。
     当远程启动服务器收到客户机SEND.FILE.REQUEST帧的要求后,会根据其远程启动数据库中的工作站记录查找对应的启动块 (boot block),并发送给工作站一个FILE.DAT.RESPONSE帧(这个帧包含了工作站启动所需的启动块),将工作站所需的启动文件传 送给工作站。
     工作站接收到完整的启动文件后,就开始执行文件中的启动程序,将执行点转向启动块的入口,启动工作站。 接着是不同的OS有不同的引导方式了,要 启动无盘win98,需要往服务器上上传win98文件,并制作启动映像文件,工作站才能读取win98的启动映像文件,进一步读取win98安装目录, 启动win98。

PXE无盘启动的基本工作原理

PXE是RPL的升级品,它是Preboot Execution Environment的缩写。它们的不同之处为:一个是静态路由,一个动态路由。不 难理解:RPL是根据网卡上的ID号加上其它的记录组成的Frame向server发出请求,而server那里早已经有了这个ID数据,匹配成功则进行 远程启动;PXE是则是根据服务器端收到的工作站MAC地址(就是网卡号),使用DHCP服务给这个MAC地址指定一个IP地址,每次重启动可能同一台工 作站有与上次启动有不同的IP,动态分配地址。下面以工作站引导过程说明PXE的原理:

1、 用户端电脑开机后, PXE Boot ROM 获得控制权之前先做自我测试,然后以广播形式发出一个请求FIND帧。
2、 如果服务器收到个人电脑所送出的要求, 就会送回 DHCP 回应, 内容包括用户端的 IP 地址, 预设通讯通道, 及开机映像文件。 否则, 服务器会忽略这个要求。
3、 工作站收到服务器发回的响应后则会回应一个FRAME,以请求传送启动所需文件。
4、之后,将有更多的讯息在用户端与服务器之间作应答, 用以决定启动参数。 BootPROM 由 TFTP 通讯协议从服务器下载开机映像档, 这个映像档就是软盘的映象文件。
5、 工作站使用TFTP 协议接收启动文件后,将控制权转交启动块,引导操作系统,完成远程启动。

Read: 41

[转]关于winpcap的一些认识

关于winpcap的一些认识关于winpcap的一些认识

                                                                         Bill yuan

                                                                                                          2004-9-29

一.Winpcap简介

       Winpcap是UNIX下的libpcap移植到windows下的产物,他是一个free and open source的项目。Winpcap工作于驱动(Driver)层,所以能以很高的效率进行网络操作。

       Winpcap提供了以下强大的功能:

1.捕获原始的数据包

2.设置filter,只捕获自己敢兴趣的数据包

3.方便的把捕获的数据包输出到文件和从文件输入

4.发送原始的数据包

5.统计网络流量

6.…..(其它还有很多,我不知道了)

二.Winpcap的安装使用方法

       1.到http://winpcap.polito.it下载winpcap的安装包,程序员开发包。

     2.执行安装包,这样你的机子就能运行winpcap程序了

     3.解压开发包,在VC的option的include和lib中加入winpcap的

include和lib

4. 在你的程序中加入#include <pcap.h>, #include <remote-ext.h>.然后在工程的setting中加入预定义宏:WPCAP,HAVE_REMOTE.导入wpcap.lib库

     5.就可以编写wpcap程序了

三.Winpcap的一些基本的功能的实现

     一)捕获数据包

              1.    枚举所有的可用的设备[pcap_findalldevs_ex](可选)

              2.    通过名字打开一个设备[pcap_open()]

在这里可以打开一个文件,只是在打开这个文件之前需要通过pcap_createsrcstr创建相应的name string

3.        设置Filter[pcap_compile, pcap_setfilter] (可选)

4.        捕获数据

有几种捕获数据的方法(捕获数据的数据都是最原始的数据包,即包含数据链路层的数据头)

a. 是以回调的方式[ pcap_loop,pcap_dispatch() ].

这两种方法基本相同,底层收集数据包,当满足一定的条件(timeout 或者缓冲区满),就会调用回调函数,把收集到的原始数据包s,交给用户。他们返回的数据缓冲区包含多个包

b.     pcap_next_ex()的方式

每当一个包到到达以后,pcap_next_ex就会返回,返回的数据缓冲区里只包涵一个包。

       二)发送包

              Winpcap中有发送单个包和发送多个包的方法。这里只说说发送单个包

1. 通过名字打开一个设备[pcap_open]

2. 自己构造一个原始数据包(这个数据包会不经过任何处理就发送出去,所以必须把包中的各个字段设置好。另外这个数据包是包含数据链路层报头的)

3. 使用pcap_sendpacket()发送数据包

三)统计网络流量

1. 通过名字打开一个设备[pcap_open]

     通过 read_timeout来设置统计的时间间隔

2. 设置filter[pcap_compile, pcap_setfilter] (可选)

3. 设置设备的为统计模式[ pcap_setmode(MODE_STAT);]

4. 开始统计,pcap_loop/pcap_dispatch()

5.在回调函数中的参数中就包含了统计信息,如下图:

     

   

四. 总结

这些东西都是我在学习winpcap的过程中的一些经验和总结。由于我学习winpcap的时间很匆忙,只是按照step by step guide来学习的,所以我对于winpcap的理解也就只能局限与此,希望能在以后有机会深入学习

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//下面是一个应用winpcap写的一个网络流量统计的例子,

// nettraffic.cpp : Defines the entry point for the console application.

//

#include "stdafx.h"

#include <pcap.h>

#include <remote-ext.h>

#include <iostream>

#include <winsock2.h>

using namespace std;

//------------------------------------------------------------------------

//------------------------------------------------------------------------

void dispatcher_handler(u_char* user_data, const struct pcap_pkthdr * pkthdr, const u_char *pktdata);

//------------------------------------------------------------------------

int main(int argc, char* argv[])

{

       int                                       i;

       pcap_if_t*                          alldevs;

       pcap_if_t*                          dev;

       char                             errorbuf[PCAP_ERRBUF_SIZE];

       int                                       choice;

       pcap_t*                              stathandle;

       WSADATA                              wsadata;

       struct timeval               timestamp;

     

       if( WSAStartup( MAKEWORD(2,2), &wsadata) != 0 )

       {

              cerr<<"WSAStartup failed [ "<<WSAGetLastError() <<" ]"<<endl;

              return (-1);

       }

     

       //enum all device

       if( pcap_findalldevs_ex( PCAP_SRC_IF_STRING, NULL, &alldevs, errorbuf ) == -1 )

       {

              WSACleanup();

              cerr<<"pcap_findalldevs_ex failed! ("<<errorbuf<<")"<<endl;

              return (-1);

       }

     

       for( i=0,dev = alldevs; dev != NULL; dev = dev->next )

       {

              cout<<++i<<'t'<<dev->name<<endl;

       }

     

       if( i== 0 )

       {

              WSACleanup();

              cerr<<"no device found!"<<endl;

              return (-2);

       }

     

       //let user choice

       while( 1)

       {

              cout<<"please choice a device:";

              cin>>choice;

              if( choice >= 1 && choice <=i )

                     break;

            

              cerr<<"input error, you shall choice a device from upon list"<<endl;

       }

     

       //move to the choice device

       for( i=0, dev = alldevs; i<choice-1; i++,dev = dev->next );

       if( (stathandle = pcap_open( dev->name,

                                                        100,

                                                        PCAP_OPENFLAG_PROMISCUOUS,

                                                        500,

                                                        NULL, errorbuf ) ) == NULL )

       {

              cerr<<"open device failed! [device:"<<dev->name<<"] "

                     <<errorbuf<<endl;

            

              pcap_freealldevs( alldevs );

              WSACleanup();

              return (-3);

       }

     

       cout<<"is Stat "<<dev->name<<" ..."<<endl;

       pcap_freealldevs( alldevs );

     

       pcap_setmode( stathandle, MODE_STAT );

       timestamp.tv_sec = 0;

       timestamp.tv_usec = 0;

       pcap_loop( stathandle, 0, dispatcher_handler,(unsigned char*)&timestamp );

       pcap_close( stathandle );

       return 0;

}

//------------------------------------------------------------------------

void dispatcher_handler(u_char* user_data, const struct pcap_pkthdr * pkthdr, const u_char *pktdata)

{

       static struct timeval       tstamp = *( (struct timeval*)user_data );

       LARGE_INTEGER                  Bps,Pps;

       unsigned long               delay;

       char                             strtime[32];

     

       delay = (pkthdr->ts.tv_sec - tstamp.tv_sec)*1000000 - tstamp.tv_usec + pkthdr->ts.tv_usec;

     

       Pps.QuadPart = ((*(LONGLONG*)(pktdata)) * 1000000 ) / delay;

       Bps.QuadPart = ((*(LONGLONG*)(pktdata + 8)) * 1000000 ) / delay;

       struct tm* ltime = localtime( &(pkthdr->ts.tv_sec) );

       strftime( strtime, sizeof(strtime),"%H:%M:%S", ltime);

     

       printf("%s:", strtime );

       printf("tPps=%I64utBps=%I64urn",Pps.QuadPart,Bps.QuadPart);

     

       tstamp = pkthdr->ts;

}

Read: 496