Netfilter源代码分析详解

Netfilter源代码分析详解
Netfilter源代码分析详解

一、概述

1. Netfilter/IPTables框架简介

Netfilter/IPTables是继2.0.x的IPfwadm、2.2.x的IPchains之后,新一代的Linux防火墙机制。Netfilter采用模块化设计,具有良好的可扩充性。其重要工具模块IPTables连接到Netfilter的架构中,并允许使用者对数据报进行过滤、地址转换、处理等操作。

Netfilter提供了一个框架,将对网络代码的直接干涉降到最低,并允许用规定的接口将其他包处理代码以模块的形式添加到内核中,具有极强的灵活性。

2. 主要源代码文件

?Linux内核版本:2.4.21

?Netfilter主文件:net/core/netfilter.c

Netfilter主头文件:include/linux/netfilter.h

?IPv4相关:

c文件:net/ipv4/netfilter/*.c

头文件:include/linux/netfilter_ipv4.h

include/linux/netfilter_ipv4/*.h

?IPv4协议栈主体的部分c文件,特别是与数据报传送过程有关的部分:ip_input.c,ip_forward.c,ip_output.c,ip_fragment.c等

二、Netfilter/IPTables-IPv4总体架构

Netfilter主要通过表、链实现规则,可以这么说,Netfilter是表的容器,表是链的容器,链是规则的容器,最终形成对数据报处理规则的实现。

详细地说,Netfilter/IPTables的体系结构可以分为三个大部分:

1. Netfilter的HOOK机制

Netfilter的通用框架不依赖于具体的协议,而是为每种网络协议定义一套HOOK函数。这些HOOK函数在数据报经过协议栈的几个关键点时被调用,在这几个点中,协议栈将数据报及HOOK函数标号作为参数,传递给Netfilter框架。

对于它在网络堆栈中增加的这些HOOK,内核的任何模块可以对每种协议的一个或多个HOOK进行注册,实现挂接。这样当某个数据报被传递给Netfilter 框架时,内核能检测到是否有任何模块对该协议和HOOK函数进行了注册。若注册了,则调用该模块的注册时使用的回调函数,这样这些模块就有机会检查、修改、丢弃该数据报及指示Netfilter将该数据报传入用户空间的队列。

这样,HOOK提供了一种方便的机制:在数据报通过Linux内核的不同位置上截获和操作处理数据报。

2. IPTables基础模块

IPTables基础模块实现了三个表来筛选各种数据报,具体地讲,Linux2.4

内核提供的这三种数据报的处理功能是相互间独立的模块,都基于Netfilter的HOOK函数和各种表、链实现。这三个表包括:filter表,nat表以及mangle表。

3. 具体功能模块

1.

1.数据报过滤模块

2.连接跟踪模块(Conntrack)

3.网络地址转换模块(NAT)

4.数据报修改模块(mangle)

5.其它高级功能模块

于是,Netfilter/IPTables总体架构如图

https://www.360docs.net/doc/f614711530.html,/photo/24896_061206192251.jpg所示

三、HOOK的实现

1. Netfilter-IPv4中的HOOK

Netfilter模块需要使用HOOK来启用函数的动态钩接,它在IPv4中定义了五个HOOK(位于文件include/linux/netfilter_ipv4.h,Line 39),分别对应0-4

的hooknum

简单地说,数据报经过各个HOOK的流程如下:

数据报从进入系统,进行IP校验以后,首先经过第一个HOOK函数

NF_IP_PRE_ROUTING进行处理;然后就进入路由代码,其决定该数据报是需要转发还是发给本机的;若该数据报是发被本机的,则该数据经过HOOK函数NF_IP_LOCAL_IN处理以后然后传递给上层协议;若该数据报应该被转发则它被NF_IP_FORWARD处理;经过转发的数据报经过最后一个HOOK函数

NF_IP_POST_ROUTING处理以后,再传输到网络上。本地产生的数据经过HOOK函数NF_IP_LOCAL_OUT 处理后,进行路由选择处理,然后经过

NF_IP_POST_ROUTING处理后发送出去。

总之,这五个HOOK所组成的Netfilter-IPv4数据报筛选体系如图

https://www.360docs.net/doc/f614711530.html,/photo/24896_061206192311.jpg:(注:下面所说Netfilter/IPTables均基于IPv4,不再赘述)

详细地说,各个HOOK及其在IP数据报传递中的具体位置如图

https://www.360docs.net/doc/f614711530.html,/photo/24896_061206192340.jpg

?NF_IP_PRE_ROUTING (0)

数据报在进入路由代码被处理之前,数据报在IP数据报接收函数ip_rcv()(位于net/ipv4/ip_input.c,Line379)的最后,也就是在传入的数据报被处理之前经过这个HOOK。在ip_rcv()中挂接这个HOOK之前,进行的是一些与类型、长度、版本有关的检查。

经过这个HOOK处理之后,数据报进入ip_rcv_finish()(位于

net/ipv4/ip_input.c,Line306),进行查路由表的工作,并判断该数据报是发给本地机器还是进行转发。

在这个HOOK上主要是对数据报作报头检测处理,以捕获异常情况。

涉及功能(优先级顺序):Conntrack(-200)、mangle(-150)、DNAT(-100)?NF_IP_LOCAL_IN(1)

目的地为本地主机的数据报在IP数据报本地投递函数ip_local_deliver()(位于net/ipv4/ip_input.c,Line290)的最后经过这个HOOK。

经过这个HOOK处理之后,数据报进入ip_local_deliver_finish()(位于

net/ipv4/ip_input.c,Line219)

这样,IPTables模块就可以利用这个HOOK对应的INPUT规则链表来对数据报进行规则匹配的筛选了。防火墙一般建立在这个HOOK上。

涉及功能:mangle(-150)、filter(0)、SNAT(100)、Conntrack(INT_MAX-1)?NF_IP_FORWARD(2)

目的地非本地主机的数据报,包括被NAT修改过地址的数据报,都要在IP 数据报转发函数ip_forward()(位于net/ipv4/ip_forward.c,Line73)的最后经过这个HOOK。

经过这个HOOK处理之后,数据报进入ip_forward_finish()(位于

net/ipv4/ip_forward.c,Line44)

另外,在net/ipv4/ipmr.c中的ipmr_queue_xmit()函数(Line1119)最后也会经过这个HOOK。(ipmr为多播相关,估计是在需要通过路由转发多播数据时的处理)

这样,IPTables模块就可以利用这个HOOK对应的FORWARD规则链表来对数据报进行规则匹配的筛选了。

涉及功能:mangle(-150)、filter(0)

?NF_IP_LOCAL_OUT (3)

本地主机发出的数据报在IP数据报构建/发送函数ip_queue_xmit()(位于net/ipv4/ip_output.c,Line339)、以及ip_build_and_send_pkt()(位于

net/ipv4/ip_output.c,Line122)的最后经过这个HOOK。(在数据报处理中,前者最为常用,后者用于那些不传输有效数据的SYN/ACK包)

经过这个HOOK处理后,数据报进入ip_queue_xmit2()(位于

net/ipv4/ip_output.c,Line281)

另外,在ip_build_xmit_slow()(位于net/ipv4/ip_output.c,Line429)和

ip_build_xmit()(位于net/ipv4/ip_output.c,Line638)中用于进行错误检测;在igmp_send_report()(位于net/ipv4/igmp.c,Line195)的最后也经过了这个HOOK,进行多播时相关的处理。

这样,IPTables模块就可以利用这个HOOK对应的OUTPUT规则链表来对数据报进行规则匹配的筛选了。

涉及功能:Conntrack(-200)、mangle(-150)、DNAT(-100)、filter(0)?NF_IP_POST_ROUTING (4)

所有数据报,包括源地址为本地主机和非本地主机的,在通过网络设备离开本地主机之前,在IP数据报发送函数ip_finish_output()(位于net/ipv4/ip_output.c,Line184)的最后经过这个HOOK。

经过这个HOOK处理后,数据报进入ip_finish_output2()(位于

net/ipv4/ip_output.c,Line160)另外,在函数ip_mc_output()(位于

net/ipv4/ip_output.c,Line195)中在克隆新的网络缓存skb时,也经过了这个HOOK 进行处理。

涉及功能:mangle(-150)、SNAT(100)、Conntrack(INT_MAX)

其中,入口为net_rx_action()(位于net/core/dev.c,Line1602),作用是将数据报一个个地从CPU的输入队列中拿出,然后传递给协议处理例程。

出口为dev_queue_xmit()(位于net/core/dev.c,Line1035),这个函数被高层协议的实例使用,以数据结构struct sk_buff *skb的形式在网络设备上发送数据报。

2. HOOK的调用

HOOK的调用是通过宏NF_HOOK实现的,其定义位于

include/linux/Netfilter.h,Line122:

#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) /

(list_empty(&nf_hooks[(pf)][(hook)]) /

? (okfn)(skb) /

: nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))

这里先调用list_empty函数检查HOOK点存储数组nf_hooks是否为空,为空则表示没有HOOK注册,则直接调用okfn继续处理。如果不为空,则转入

nf_hook_slow()函数。

nf_hook_slow()函数(位于net/core/netfilter.c,Line449)的工作主要是读

nf_hook数组遍历所有的nf_hook_ops结构,并调用nf_hookfn()处理各个数据报。

即HOOK的调用过程如图

https://www.360docs.net/doc/f614711530.html,/photo/24896_061206192356.jpg所示

下面说明一下NF_HOOK的各个参数:

pf:协议族标识,相关的有效协议族列表位于include/linux/socket.h,Line 178。对于IPv4,应该使用协议族PF_INET;

?hook:HOOK标识,即前面所说5个HOOK对应的hooknum;

?skb:是含有需要被处理包的sk_buuff数据结构的指针。sk_buff是Linux 网络缓存,指那些linux内核处理IP分组报文的缓存,即套接字缓冲区。

网卡收到IP分组报文后,将它们放入sk_buff,然后再传送给网络堆栈,网络堆栈几乎一直要用到sk_buff。其定义在include/linux/skbuff.h,Line 129,下面列出我认为对分析有意义的部分成员:

?

o`struct sock *sk;`:指向创建分组报文的socket;

o`struct timeval stamp;`:分组报文到达系统的时间;

o下面是三个union,存放的是各层中各种协议的报文头指针:

?h对应传输层的报头

?nh对应网络层的报头

?mac对应MAC层的报头

o`unsigned int len;`:套接字缓存所代表的报文长度,即从`unsigned char *data;`的位置算起的当前有效报文长度。

o`unsigned char pkt_type,`:表示报文的类型,具体类型定义在

include/linux/if_packet.h,Line24:

#define PACKET_HOST 0 // 发送到本机的报文

#define PACKET_BROADCAST 1 // 广播报文

#define PACKET_MULTICAST 2 // 多播报文

#define PACKET_OTHERHOST 3 // 表示目的地非本机但被本机接收的报文

#define PACKET_OUTGOING 4 // 离开本机的报文

/* These ones are invisible by user level */

#define PACKET_LOOPBACK 5 // 本机发给自己的报文

#define PACKET_FASTROUTE 6 // 快速路由报文

?indev:输入设备,收到数据报的网络设备的net_device数据结构指针,即数据报到达的接口。

o用于NF_IP_PRE_ROUTING和NF_IP_LOCAL_IN两个HOOK ?outdev:输出设备,数据报离开本地所要使用的网络设备的net_device数据结构指针。

o用于NF_IP_LOCAL_OUT和NF_IP_POST_ROUTING两个HOOK

o注意:在通常情况下,在一次HOOK调用中,indev和outdev中只有一个参数会被使用

?okfn:下一步要处理的函数。即如果有HOOK函数,则处理完所有的HOOK 函数,且所有向该HOOK注册过的筛选函数都返回NF_ACCEPT时,调用这个函数继续处理;如果没有注册任何HOOK,则直接调用此函数。

其5个参数将由宏NF_HOOK传入。

3. HOOK点的实现

对应于各个不同协议的不同HOOK点是由一个二维数组nf_hooks存储的(位于net/core/netfilter.c,Line 47),具体的HOOK点则由数据结构nf_hook_ops (位于include/linux/netfilter.h,Line 44)实现。如图

https://www.360docs.net/doc/f614711530.html,/photo/24896_061206192528.jpg所示:

其中,nf_hook_ops成员中:

?`int priority;` priority值越小,优先级越高,相关优先级在

include/linux/netfilter_ipv4.h,Line52中枚举定义:

enum NF_IP_hook_priorities {

NF_IP_PRI_FIRST = INT_MIN,

NF_IP_PRI_CONNTRACK= -200,

NF_IP_PRI_MANGLE = -150,

NF_IP_PRI_NAT_DST = -100,

NF_IP_PRI_FILTER = 0,

NF_IP_PRI_NAT_SRC = 100,

NF_IP_PRI_LAST = INT_MAX,

};

?`nf_hookfn *hook;` 为处理函数的指针,其函数指针类型定义位于include/linux/netfilter.h,Line38,为:

typedef unsigned int nf_hookfn(unsigned int hooknum,

struct sk_buff **skb,

const struct net_device *in,

const struct net_device *out,

int (*okfn)(struct sk_buff *));

这是nf_hook_ops中最关键的成员,其五个参数分别对应前面所解释的

NF_HOOK中弟2到6个参数

调用HOOK的包筛选函数必须返回特定的值,这些值以宏的形式定义于头文件include/linux/netfilter.h中(Line15),分别为:

o NF_DROP(0):丢弃此数据报,禁止包继续传递,不进入此后的处理流程;

o NF_ACCEPT(1):接收此数据报,允许包继续传递,直至传递到链表最后,而进入okfn函数;

?以上两个返回值最为常见

o NF_STOLEN(2):数据报被筛选函数截获,禁止包继续传递,但并不释放数据报的资源,这个数据报及其占有的sk_buff仍然有效

(e.g. 将分片的数据报一一截获,然后将其装配起来再进行其他处

理);

o NF_QUEQUE(3):将数据报加入用户空间队列,使用户空间的程序可以直接进行处理;

?在nf_hook_slow()以及nf_reinject()函数(位于

net/core/netfilter.c,Line449,Line505)中,当由调用nf_iterate()

函数(位于net/core/netfilter.c,Line339,作用为遍历所有注

册的HOOK函数,并返回相应的NF_XX值)而返回的verdict

值为NF_QUEUE时(即当前正在执行的这个HOOK筛选函

数要求将数据报加入用户空间队列),会调用nf_queue()函

数(位于net/core/netfilter.c,Line407)

?nf_queue()函数将这个数据报加入用户空间队列nf_info(位

于include/linux/netfilter.h,Line77),并保存其设备信息以

备用

o NF_REPEAT(4):再次调用当前这个HOOK的筛选函数,进行重复处理。

4. HOOK的注册和注销

HOOK的注册和注销分别是通过nf_register_hook()函数和

nf_unregister_hook()函数(分别位于net/core/netfilter.c,Line60,76)实现的,其参数均为一个nf_hook_ops结构,二者的实现也非常简单。

nf_register_hook()的工作是首先遍历nf_hools[][],由HOOK的优先级确定在HOOK链表中的位置,然后根据优先级将该HOOK的nf_hook_ops加入链表;

nf_unregister_hook()的工作更加简单,其实就是将该HOOK的nf_hook_ops 从链表中删除。

四、IPTables系统

1. 表-规则系统

IPTables是基于Netfilter基本架构实现的一个可扩展的数据报高级管理系统,利用table、chain、rule三级来存储数据报的各种规则。系统预定义了三个table:

?filter:数据报过滤表(文件net/ipv4/netfilter/iptable_filter.c)

监听NF_IP_LOCAL_IN、NF_IP_FORWARD和NF_IP_LOCAL_OUT三个HOOK,作用是在所有数据报传递的关键点上对其进行过滤。

?nat:网络地址转换表

监听NF_IP_PRE_ROUTING、NF_IP_POST_ROUTING和NF_IP_LOCAL_OUT

三个HOOK,作用是当新连接的第一个数据报经过时,在nat表中决定对其的转换操作;而后面的其它数据报都将根据第一个数据报的结果进行相同的转换处理。

?mangle:数据报修改表(位于net/ipv4/netfilter/iptable_mangle.c)

监听NF_IP_PRE_ROUTING和NF_IP_LOCAL_OUT两个HOOK,作用是修改

数据报报头中的一些值。

2. 表的实现

?表的基本数据结构是ipt_table(位于include/linux/netfilter_ipv4/ip_tables.h,Line413):

struct ipt_table

{

struct list_head list; // 一个双向链表

char name[IPT_TABLE_MAXNAMELEN]; // 被用户空间使用的表函数的名字struct ipt_replace *table; // 表初始化的模板,定义了一个初始化用的该// 表的所

默认的HOOK所包含的规则等信息,

// 用户通过系统调用进行表的替换时也要用

unsigned int valid_hooks; // 表所监听的HOOK,实质是一个位图

rwlock_t lock; // 整个表的读/写自旋锁

struct ipt_table_info *private; // 表所存储的数据信息,也就是实际的数据区,

// 仅在处理ipt_table的代码内部使用

struct module *me; // 如果是模块,那么取THIS_MODULE,否则取NULL

};

其中:

?`unsigned int valid_hooks;`这个位图有两个作用:一是检查Netfilter中哪些HOOK对应着合法的entries;二是用来为ipt_match以及ipt_target数据结构中的checkentry()函数核算可能的HOOK。

?`struct module *me;`当取值为THIS_MODULE时,可以阻止用户rmmod 一个仍然被某个规则指向的模块的尝试。

?`struct ipt_replace *table;`的数据结构是被用户空间用来替换一个表的,其定义位于include/linux/netfilter_ipv4/ip_tables.h,Line230:

struct ipt_replace

{

char name[IPT_TABLE_MAXNAMELEN];

unsigned int valid_hooks;

unsigned int num_entries; // 规则表入口的数量

unsigned int size; // 新的规则表的总大小

/* Hook entry points. */

unsigned int hook_entry[NF_IP_NUMHOOKS]; // 表所监听HOOK的规则入口,// 是对于entries[ ]的偏移

unsigned int underflow[NF_IP_NUMHOOKS]; // 规则表的最大下界

unsigned int num_counters; // 旧的计数器数目,即当前的旧entries的数目

struct ipt_counters *counters; // 旧的计数器

struct ipt_entry entries[0]; // 规则表入口

};

?上文所提到的filter、nat和mangle表分别是ipt_table这个数据结构的三个实例:packet_filter(位于net/ipv4/netfilter/iptable_filter.c,Line84)、nat_table (位于net/ipv4/netfilter/ip_nat_rule.c,Line104)以及packet_mangler(位于net/ipv4/netfilter/iptable_mangle.c,Line117)

?ipt_table_info(位于net/ipv4/netfilter/ip_tables.c,Line86)是实际描述规则表的数据结构:

struct ipt_table_info

{

unsigned int size;

unsigned int number; // 表项的数目

unsigned int initial_entries; // 初始表项数目

unsigned int hook_entry[NF_IP_NUMHOOKS]; // 所监听HOOK的规则入口unsigned int underflow[NF_IP_NUMHOOKS]; // 规则表的最大下界

char entries[0] ____cacheline_aligned; // 规则表入口,即真正的规则存储结构// ipt_entry组成块的起始地址,对多CPU,每个CPU对应一个

};

3. 规则的实现

IPTables中的规则表可以在用户空间中使用,但它所采用的数据结构与内核空间中的是一样的,只不过有些成员不会在用户空间中使用。

一个完整的规则由三个数据结构共同实现,分别是:

?

o一个ipt_entry结构,存储规则的整体信息;

o0或多个ipt_entry_match结构,存放各种match,每个结构都可以存放任意的数据,这样也就拥有了良好的可扩展性;

o1个ipt_entry_target结构,存放规则的target,类似的,每个结构也可以存放任意的数据。

下面将依次对这三个数据结构进行分析:

i.存储规则整体的结构ipt_entry,其形式是一个链表(位于

include/linux/netfilter_ipv4/ip_tables.h,Line122):

struct ipt_entry

{

struct ipt_ip ip;

unsigned int nfcache;

u_int16_t target_offset;

u_int16_t next_offset;

unsigned int comefrom;

struct ipt_counters counters;

unsigned char elems[0];

};

其成员如下:

?

o`struct ipt_ip ip;`:这是对其将要进行匹配动作的IP数据报报头的描述,其定义于include/linux/netfilter_ipv4/ip_tables.h,Line122,

其成员包括源/目的IP及其掩码,出入端口及其掩码,协议族、标

志/取反flag等信息。

o`unsigned int nfcache;`:HOOK函数返回的cache标识,用以说明经过这个规则后数据报的状态,其可能值有三个,定义于

include/linux/netfilter.h,Line23:

#define NFC_ALTERED 0x8000 //已改变

#define NFC_UNKNOWN 0x4000 //不确定

另一个可能值是0,即没有改变。

?

o`u_int16_t target_offset;`:指出了target的数据结构ipt_entry_target 的起始位置,即从ipt_entry的起始地址到match存储结束的位置o`u_int16_t next_offset;`:指出了整条规则的大小,也就是下一条规则的起始地址,即ipt_entry的起始地址到match偏移再到target存

储结束的位置

o`unsigned int comefrom;`:所谓的“back pointer”,据引用此变量的代码(主要是net/ipv4/netfilter/ip_tables.c中)来看,它应该是指向

数据报所经历的上一个规则地址,由此实现对数据报行为的跟踪o`struct ipt_counters counters;`:说明了匹配这个规则的数据报的计数以及字节计数(定义于include/linux/netfilter_ipv4/ip_tables.h,

Line100)

o`unsigned char elems[0];`:表示扩展的match开始的具体位置(因为它是大小不确定的),当然,如果不存在扩展的match那么就是

target的开始位置

i.扩展match的存储结构ipt_entry_match,位于

include/linux/netfilter_ipv4/ip_tables.h,Line48:

struct ipt_entry_match

{

union {

struct {

u_int16_t match_size;

char name[IPT_FUNCTION_MAXNAMELEN];

} user;

struct {

u_int16_t match_size;

struct ipt_match *match;

} kernel;

u_int16_t match_size; //总长度

} u;

unsigned char data[0];

};

其中描述match大小的`u_int16_t match_size;`,从涉及这个变量的源码看来,在使用的时候需要注意使用一个宏IPT_ALIGN(位于

include/linux/netfilter_ipv4/ip_tables.h,Line445)来进行4的对齐处理(0x3 &

0xfffffffc),这应该是由于match、target扩展后大小的不确定性决定的。

在结构中,用户空间与内核空间为不同的实现,内核空间中的描述拥有更多的信息。在用户空间中存放的仅仅是match的名称,而在内核空间中存放的则是一个指向ipt_match结构的指针

结构ipt_match位于include/linux/netfilter_ipv4/ip_tables.h,Line342:

struct ipt_match

{

struct list_head list;

const char name[IPT_FUNCTION_MAXNAMELEN];

int (*match)(const struct sk_buff *skb,

const struct net_device *in,

const struct net_device *out,

const void *matchinfo, // 指向规则中match数据的指针,

// 具体是什么数据结构依情况而定

int offset, // IP数据报的偏移

const void *hdr, // 指向协议头的指针

u_int16_t datalen, // 实际数据长度,即数据报长度-IP头长度

int *hotdrop);

int (*checkentry)(const char *tablename, // 可用的表

const struct ipt_ip *ip,

void *matchinfo,

unsigned int matchinfosize,

unsigned int hook_mask); // 对应HOOK的位图

void (*destroy)(void *matchinfo, unsigned int matchinfosize);

struct module *me;

};

其中几个重要成员:

?

o`int (*match)(……);`:指向用该match进行匹配时的匹配函数的指针,match相关的核心实现。返回0时hotdrop置1,立即丢弃数据

报;返回非0表示匹配成功。

o`int (*checkentry)(……);`:当试图插入新的match表项时调用这个指针所指向的函数,对新的match表项进行有效性检查,即检查参

数是否合法;如果返回false,规则就不会被接受(譬如,一个TCP

的match只会TCP包,而不会接受其它)。

o`void (*destroy)(……);`:当试图删除一个使用这个match的表项时,即模块释放时,调用这个指针所指向的函数。我们可以在checkentry

中动态地分配资源,并在destroy中将其释放。

i.扩展target的存储结构ipt_entry_target,位于

include/linux/netfilter_ipv4/ip_tables.h,Line71,这个结构与ipt_entry_match 结构类似,同时其中描述内核空间target的结构ipt_target(位于

include/linux/netfilter_ipv4/ip_tables.h,Line375)也与ipt_match类似,只

不过其中的target()函数返回值不是0/1,而是verdict。

而target的实际使用中,是用一个结构ipt_standard_target专门来描述,这才是实际的target描述数据结构(位于include/linux/netfilter_ipv4/ip_tables.h,

Line94),它实际上就是一个ipt_entry_target加一个verdict。

?其中成员verdict这个变量是一个很巧妙的设计,也是一个非常重要的东东,其值的正负有着不同的意义。我没有找到这个变量的中文名称,在内核开发者的新闻组中称这个变量为“a magic number”。它的可能值包括

IPT_CONTINUE、IPT_RETURN以及前文所述的NF_DROP等值,那么

它的作用是什么呢?

?

o由于IPTables是在用户空间中执行的,也就是说Netfilter/IPTables 这个框架需要用户态与内核态之间的数据交换以及识别。而在具体

的程序中,verdict作为`struct ipt_standard_target`的一个成员,也是

对于`struct ipt_entry_target`中的target()函数的返回值。这个返回值

标识的是target()所对应的执行动作,包括系统的默认动作以及外

部提交的自定义动作。

o但是,在用户空间中提交的数据往往是类似于“ACCPET”之类的字符串,在程序处理时则是以`#define NF_ACCEPT 1`的形式来进

行的;而实际上,以上那些执行动作是以链表的数据结构进行存储

的,在内核空间中表现为偏移。

o于是,verdict实际上描述了两个本质相同但实现不同的值:一个是用户空间中的执行动作,另一个则是内核空间中在链表中的偏移

——而这就出现了冲突。

o解决这种冲突的方法就是:用正值表示内核中的偏移,而用负值来表示数据报的那些默认动作,而外部提交的自定义动作则也是用正

值来表示。这样,在实际使用这个verdict时,我们就可以通过判

断值的正负来进行相应的处理了。

o位于net/ipv4/netfilter/ip_tables.h中的函数ipt_do_table()中有一个典型的verdict使用(Line335,其中v是一个verdict的实例):

if (v !=IPT_RETURN) {

verdict = (unsigned)(-v) - 1;

break;

}

其中的IPT_RETURN定义为:

#define IPT_RETURN (-NF_MAX_VERDICT – 1)

而宏NF_MAX_VERDICT实际上就是:

#define NF_MAX_VERDICT NF_REPEAT

这样,实际上IPT_RETURN的值就是-NF_REPEAT-1,也就是对应REPEAT,这就是对执行动作的实际描述;而我们可以看到,在下面对verdict进行赋值时,它所使用的值是`(unsigned)(-v) – 1`,这就是在内核中实际对偏移进行定位时所使用的值。

那么总之呢,表和规则的实现如图

https://www.360docs.net/doc/f614711530.html,/photo/24896_061206192551.jpg所示:

从上图中不难发现,match的定位如下:

o起始地址为:当前规则(起始)地址+sizeof(struct ipt_entry);

o结束地址为:当前规则(起始)地址+ipt_entry->target_offset;

o每一个match的大小为:ipt_entry_match->u.match_size。

target的定位则为:

?

o起始地址为match的结束地址,即:当前规则(起始)地址+ipt_entry-> target_offset;

o结束地址为下一条规则的起始地址,即:当前规则(起始)地址+ipt_entry-> next_offset;

o每一个target的大小为:ipt_entry_target->u.target_size。

这些对于理解match以及target相关函数的实现是很有必要明确的。

同时,include/linux/netfilter_ipv4/ip_tables.h中提供了三个“helper functions”,可用于使对于entry、tartget和match的操作变得方便,分别是:

?函数ipt_get_target():Line274,作用是取得target的起始地址,也就是上面所说的当前规则(起始)地址+ipt_entry-> target_offset;

?宏IPT_MATCH_ITERATE():Line281,作用是遍历规则的所有match,并执行同一个(参数中)给定的函数。其参数为一个ipt_entry_match结构

和一个函数,以及函数需要的参数。当返回值为0时,表示遍历以及函数

执行顺利完成;返回非0值时则意味着出现问题已终止。

?宏IPT_ENTRY_ITERATE():Line300,作用是遍历一个表中的所有规则,并执行同一个给定的函数。其参数为一个ipt_entry结构、整个规则表的

大小,以及一个函数和其所需参数。其返回值的意义与宏

IPT_MATCH_ITERATE()类似。

o那么如何保证传入的ipt_entry结构是整个规则表的第一个结构呢?

据源码看来,实际调用这个宏的时候传入的第一个参数都是某个

ipt_table_info结构的实例所指向的entries成员,这样就保证了对整

个规则表的完整遍历。

4. 规则的使用

当一个特定的HOOK被激活时,数据报就开始进入Netfilter/IPTables系统

进行遍历,首先检查`struct ipt_ip ip`,然后数据报将依次遍历各个match,也就

是`struct ipt_entry_match`,并执行相应的match函数,即ipt_match结构中的*match 所指向的函数。当match函数匹配不成功时返回0,或者hotdrop被置为1时,

遍历将会停止。

对match的遍历完成后,会开始检查`struct ipt_entry_target`,其中如果是一

个标准的target,那么会检查`struct ipt_standard_target`中的verdict,如果verdict

值是正的而偏移却指向不正确的位置,那么ipt_entry中的comefrom成员就有了用武之地——数据报返回所经历的上一个规则。对于非标准的target呢,就会调用target()函数,然后根据其返回值进行后面的处理。

5. 规则的扩展

Netfilter/IPTables提供了对规则进行扩展的机制:可以写一个LKM来扩展内核空间的功能,也可以写一个共享库来扩展用户空间中IPTables的功能。

1.内核的扩展

要对内核空间的功能进行扩展,实际上就是写一个具有表、match以及target 增加功能的模块,相关的函数为(位于net/ipv4/netfilter/ip_tables.c,Line1318 to 1444):

o ipt_register_table()、ipt_unregister_table(),参数为struct ipt_table *。

?ipt_register_table()函数是这三对函数中最复杂的一个,涉及

了内存、信号量等方方面面的东西,但总起来说就做了初始

化表以及加入双向链表两件事。

?其复杂一是因为涉及到多CPU的处理(每个CPU拥有各自

独立的“规则空间”),需要首先将新的entries放入第一

个CPU空间,在检查完毕后再复制到其他CPU中;二是就

是上面所说对新table各个entry的检查,包括边界检查以及

完整性检查等。

?其中的重要函数有这么几个:

?translate_table()(位于net/ipv4/netfilter/ip_tables.c,

Line797):这个函数的主要作用是检查并应用用户

空间传来的规则:

1.

1.

1.

1.对新表进行边界检查(由宏IPT_ENTRY_ITERATE()

调用函数check_entry_size_and_blocks(),位于

net/ipv4/netfilter/ip_tables.c,Line732),包括对齐、

过大过小等,特别是保证赋给hook_entries和

underflows值的正确性。

2.调用函数make_source_chains()(位于

net/ipv4/netfilter/ip_tables.c,Line499)检查新的表中

是否存在规则环,同时将HOOK的规则遍历顺序存

入comefrom变量。(这个函数我没有仔细看,只是

大概略了一下)

3.对ipt_entry依次进行ipt_ip头、match以及target的

完整性检查(由宏IPT_ENTRY_ITERATE()调用函数

check_entry(),位于net/ipv4/netfilter/ip_tables.c,

Line676),保证ipt_entry的正确性。

4.将正确的ipt_tables复制给其他的CPU。

这个函数另外还在do_replace()函数(仅在一个源码中没有被调用过的函数中被

调用,不予分析)中被调用。

?

o

?

?replace_table()(位于net/ipv4/netfilter/ip_tables.c,

Line877):这个函数的主要作用是:将得到模块初

始值的ipt_table_info结构(newinfo)中的值传给

ipt_table中的private,并返回ip_table中旧的private。

?list_prepend()(位于

include/linux/netfilter_ipv4/listhelp.h,Line75):在这

个函数被调用之前,整个初始化的过程就已经结束了,

这个函数的主要作用是:互斥地调用Linux源码中的

list_add()函数(位于include/linux/list.h,Line55),

将新的table加入到双向链表之中。

?

o ipt_register_match()、ipt_unregister_match(),参数为struct ipt_match *。

o ipt_register_target()、ipt_unregister_target(),参数为struct ipt_target *。

这三对函数除了ipt_register_table()外的5个函数主要就是互斥地将

table/match/target加入到双向链表中或者从双向链表中删除。

其中向双向链表中加入新节点是通过调用list_named_insert()函数(位于include/linux/netfilter_ipv4/listhelp.h,Line101)实现的。这个函数的主要工作是

首先确定待插入的match名字是否已经存在,只有不存在时才进行插入的操作。

1.用户空间的扩展

用户空间中的扩展用的是共享库配合libiptc库的机制,但这种机制是在单独的IPTbales程序中提供的,内核源码中并没有提供,这里就不做分析了。

五、数据报过滤模块——filter表

1. 概述

filter表的功能仅仅是对数据报进行过滤,并不对数据报进行任何的修改。

filter模块在Netfilter中是基于下列HOOK点的:

?

o NF_IP_LOCAL_IN

o NF_IP_FORWARD

o NF_IP_LOCAL_OUT

这几个HOOK分别对应着filter表中的INPUT、FORWARD、OUTPUT三条规则链,对于任何一个数据报都会经过这3个HOOK之一。

filter模块的接口位于文件net/ipv4/netfilter/iptables_filter.c中。

2. filter表的定义和初始化

filter表是前面所述数据结构ipt_table的一个实例,它的定义和初始化位于net/ipv4/netfilter/iptable_filter.c,Line84:

static struct ipt_table packet_filter

= { { NULL, NULL }, "filter", &initial_table.repl,

FILTER_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL, THIS_MODULE };

对照结构ipt_table的定义,我们可以发现,filter表的初始化数据为:

?链表初始化为空

?表名为filter

?初始化的模板为&initial_table.repl

o初始化的模板表定义于net/ipv4/netfilter/iptable_filter.c,Line30,是一个很简单的数据结构,只是赋值有些复杂,因为要对所涉及的各

个HOOK进行不同的处理:

static struct

{

struct ipt_replace repl;

struct ipt_standard entries[3];

struct ipt_error term;

} initial_table __initdata

= { { "filter", FILTER_VALID_HOOKS, 4,

sizeof(struct ipt_standard) * 3 + sizeof(struct ipt_error),

Android源代码结构分析

目录 一、源代码结构 (2) 第一层次目录 (2) bionic目录 (3) bootloader目录 (5) build目录 (7) dalvik目录 (9) development目录 (9) external目录 (13) frameworks目录 (19) Hardware (20) Out (22) Kernel (22) packages目录 (22) prebuilt目录 (27) SDK (28) system目录 (28) Vendor (32)

一、源代码结构 第一层次目录 Google提供的Android包含了原始Android的目标机代码,主机编译工具、仿真环境,代码包经过解压缩后,第一级别的目录和文件如下所示: . |-- Makefile (全局的Makefile) |-- bionic (Bionic含义为仿生,这里面是一些基础的库的源代码) |-- bootloader (引导加载器),我们的是bootable, |-- build (build目录中的内容不是目标所用的代码,而是编译和配置所需要的脚本和工具) |-- dalvik (JAVA虚拟机) |-- development (程序开发所需要的模板和工具) |-- external (目标机器使用的一些库) |-- frameworks (应用程序的框架层) |-- hardware (与硬件相关的库) |-- kernel (Linux2.6的源代码) |-- packages (Android的各种应用程序) |-- prebuilt (Android在各种平台下编译的预置脚本) |-- recovery (与目标的恢复功能相关) `-- system (Android的底层的一些库)

yaffs2文件系统制作

交叉编译器ARM-Linux-gcc4.1.2 开发板TX2440A Busybox-1.15.1.tar.bz2(在Linux中被称为瑞士军刀) mkyaffs2image工具 首先创建一个名字为root_2.6.31的文件夹,在其中创建如下文件夹 etc bin var dev home lib mnt proc root sbin sys tmp usr opt共14个文件夹 解压Busybox tar xjvf busybox 进入源目录,修改Makefile 第164行,CROSS_COMPILE=arm-linux- 第190行,ARCH=arm 执行#make men onfig进行配置 配置选项大部分都是保持默认的,只需要注意选择以下这几个选项,其他的选项都不用动:Busybox Setting---> Build Options---> [*]Build Busybox as a static binary(no shared libs) [*]Build with Large File Support(for accessing files>2GB) Installation Options--->

(./_install)Busybox installation prefix 进入这个选项,输入busybox的安装路径,如:../rootfs Busybox Library Tuning---> [*]vi-style line editing commands [*]Fancy shell prompts 要选择这个选项:“Fancy shell prompts”,否则挂载文件系统后,无法正常显示命令提示符:“[\u@\h\W]#” 配置完成以后 执行#make #make install 然后就会在上一级目录下生成rootfs文件夹,里面包含几个文件夹/bin/sbin/usr linuxrc 把这些文件全部复制到刚建好的root_2.6.31目录下, #cp–rf*../root_2.6.31 在dev目录下,创建两个设备节点: #mknod console c51 #mknod null c13 然后进入自己建立的etc目录 拷贝Busybox-1.15.2/examples/bootfloopy/etc/*到当前目录下。 #cp-r../../busybox-1.15.2/examples/bootfloopy/etc/*./ 包括文件:fstab init.d inittab profile

linux-2.6.18移植

Linux-2.6.18移植 有了我们的交叉编译环境和我们先前学的内核基础知识,下面我们就开始我们的内核移植了,我们所用的是博创的 S3C2410 。 关于 linux-2.6.18.tar.bz2 的下载网站先前我们说过,我们要先到该官方网站上去下载一个全新的内核。 [root@Binnary ~ ]# tar –jxvf linux-2.6.18.tar.bz2 [root@Binnary ~ ]# make mrproper 如果你是新下载的内核,那这一步就不用了。但如果你用的是别人移植好的内核,那最好在编译内核之前先清除一下中间文件,因为你们用来编译内核的交叉编译工具可能不同。 第一步:修改Makefile文件 将 改为 第二步:修改分区设置信息 我们要先在BootLoader中查看相应的分区信息 vivi>help 然后修改内核源码中的分区信息。分区信息文件在 a rch/arm/mach-s3c2410/common-smdk.c 将其中的

改为如下内容:

第三步:内核通过 BootLoader把数据写入NAND Flash,而vivi的ECC效验算法和内核的不同,内核的效验码是由NAND Flash控制器产生的,所以在此必须禁用NAND Flash ECC。所以我们就要修改 drivers/mtd/nand/s3c2410.c 这个文件。将 中的 chip->ecc.mode = NAND_ECC_SOFT ,改为如下 chip->ecc.mode = NAND_ECC_NONE。

只此一处。 第四步:下面是devfs的问题,因为2.6.12内核以后取消了devfs的配置选项,缺少了它内核会找不到mtdblock设备。所以我们需要修改 fs/Kconfig 文件,或者是从2.6.12的fs/Kconfig中拷贝下面几项到2.6.18的fs/Kconfig中去,我们采用修改的方法来完成。 修改 fs/Kconfig支持devfs 。 在Pseudo filesystems 主菜单的最后添加我们所要的内容。 第五步:文件系统的支持 Yaffs 文件系统 YAFFS文件系统简介 YAFFS,Yet Another Flash File System,是一种类似于JFFS/JFFS2的专门为Flash设计 的嵌入式文件系统。与JFFS相比,它减少了一些功能,因此速度更快、占用内存更少。 YAFFS和JFFS都提供了写均衡,垃圾收集等底层操作。它们的不同之处在于: (1)、JFFS是一种日志文件系统,通过日志机制保证文件系统的稳定性。YAFFS仅仅 借鉴了日志系统的思想,不提供日志机能,所以稳定性不如JAFFS,但是资源占用少。 (2)、JFFS中使用多级链表管理需要回收的脏块,并且使用系统生成伪随机变量决定 要回收的块,通过这种方法能提供较好的写均衡,在YAFFS中是从头到尾对块搜索, 所以在垃圾收集上JFFS的速度慢,但是能延长NAND的寿命。 (3)、JFFS支持文件压缩,适合存储容量较小的系统;YAFFS不支持压缩,更适合存 储容量大的系统。 YAFFS还带有NAND芯片驱动,并为嵌入式系统提供了直接访问文件系统的API,用 户可以不使用Linux中的MTD和VFS,直接对文件进行操作。NAND Flash大多采用 MTD+YAFFS的模式。MTD( Memory Technology Devices,内存技术设备)是对Flash 操作的接口,提供了一系列的标准函数,将硬件驱动设计和系统程序设计分开。 Yaffs 文件系统内核没有集成,可以对其主页下载: https://www.360docs.net/doc/f614711530.html,/cgi-bin/viewcvs.cgi/#dirlist

Yaffs2文件系统中对NAND Flash磨损均衡的改进

Yaffs2文件系统中对NAND Flash磨损均衡的改进 摘要:针对以NAND Flash为存储介质时Yaffs2文件系统存在磨损均衡的缺陷,通过改进回收块选择机制,并在数据更新中引入冷热数据分离策略,从而改善NAND Flash的磨损均衡性能。实验借助Qemu软件建立Linux嵌入式仿真平台,从总擦除次数、最大最小擦除次数差值和块擦除次数标准差等方面进行对比。实验结果表明,在改进后的Yaffs2文件系统下NAND Flash的磨损均衡效果有明显提升,这有益于延长NAND Flash的使用寿命。 关键词: Yaffs2文件系统;NAND Flash;垃圾回收;冷热数据 0 引言 NAND Flash存储设备与传统机械磁盘相比,具有体积小、存储密度高、随机存储和读写能力强、抗震抗摔、功耗低等特点[1]。它被广泛用于智能手机、车载智能中心、平板电脑等智能终端中。近年来,以NAND Flash为存储介质的固态硬盘也得到越来越多的应用。目前Yaffs2文件系统(Yet Another Flash File System Two,Yaffs2)[1]是使用最多、可移植性最好的专用文件系统,在安卓、阿里云OS、Linux等嵌入式系统中都有使用。在Yaffs2文件系统下以NAND Flash为存储介质时存在磨损均衡的缺陷,可通过对回收块选择机制作改进和引入冷热数据分离策略来提高磨损均衡的效果。 1 Yaffs2和Nand Flash关系 这里以使用最多的Linux操作系统为实践,将Yaffs2文件系统移植到Linux操作系统中。Linux系统通常可以分为3层:应用层、内核层和设备层,其中支持NAND Flash设备的Yaffs2文件系统属于内核层,。 最上层用户应用程序通过VFS(Virtual File System)提供的统一接口,将数据更新等文件操作传递给Yaffs2。VFS代表虚拟文件系统,它为上层应用提供统一的接口。有了这些接口,应用程序只用遵循抽象后的访问规则,而不必理会底层文件系统和物理构成上的差异。然后Yaffs2通过MTD(Memory Technology Device)提供的统一访问接口对NAND Flash进行读、写和擦除操作,从而完成数据的更新或者存储操作。MTD代表内存技术设备,它为存储设备提供统一访问的接口。最终,在NAND Flash上以怎样的格式组织和存储数据由Yaffs2文件系统决定。 NAND Flash由若干块(block)组成,每个块又是由若干页(page)组成,页中含有数据区和附加区。NAND Flash的页根据状态不同,可以分为有效页、脏页、空闲页。有效页中存放有效数据,脏页中存放无效数据,空闲页是经过擦除后可以直接用于写入数据的页。NAND Flash在写入数据前需要执行擦除操作,因此数据不能直接在相同的位置更新。当一个页中数据需要更新时,必须将该页中有效数据拷贝到其他空闲页上再更新,并将原来页上的数据置为无效。随着时间的推移,许多无效页累积在存储器中使得空闲页逐渐减少。当存储器中的空闲空间不足时,启动垃圾回收操作,利用回收块选择机制从待回收块中选取满足要求的块来擦除,从而得到足够的空闲空间。NAND Flash中块的擦除次数有限,通常为10 000次~100 000次[2]。当某个块的擦除次数超过使用寿命时,该块将无法正常用于数据存储。因此,垃圾回收应利用合理的回收块选择机制,从待回收块中找到回收后能产生良好磨损均衡效果且付出较少额外代价的块来回收,从而获得足够的空闲空间用于数据更新操作。 2 Yaffs2在磨损均衡方面的缺陷 Yaffs2中回收块的选择机制[3]是从待回收块中找到有效数据最少的块来回收。回收过程中,Yaffs2能够减少有效数据的额外读和写操作。当数据更新处于均匀分布的情况下,Yaffs2表现出较好的磨损均衡效果。 但是,通常情况下数据的更新频率不同,有些数据经常更新,而有些数据很少更新。经

2-Linux

Linux-2.6.32.2内核在mini2440上的移植(二)---yaffs2文件系统移植 移植环境(红色粗字体字为修改后内容,蓝色粗体字为特别注意内容) 2.1, yaffs2文件系统移植 【1】获取yaffs2 源代码 现在大部分开发板都可以支持yaffs2 文件系统,它是专门针对嵌入式设备,特别是使用nand flash 作为存储器的嵌入式设备而创建的一种文件系统,早先的yaffs 仅支持小页(512byte/page)的nand flash,现在的开发板大都配备了更大容量的nand flash,它们一般是大页模式的(2K/page),使用yaffs2 就可以支持大页的nand flash,下面是yaffs2 的移植详细步骤。 在https://www.360docs.net/doc/f614711530.html,/node/346可以下载到最新的yaffs2 源代码,需要使用git工具( 安装方法见Git版本控制软件安装与使用),在命令行输入: [root@localhost ~]# cd ./linux-test [root@localhost linux-test]# git clone git://https://www.360docs.net/doc/f614711530.html,/ya ffs2 Cloning into yaffs2... remote: Counting objects: 6592, done. remote: Compressing objects: 100% (3881/3881), done. remote: Total 6592 (delta 5237), reused 3396 (delta 2642) Receiving objects: 100% (6592/6592), 3.34 MiB | 166 KiB/s, d one. Resolving deltas: 100% (5237/5237), done.

使用yaffs2img工具制作Android刷机包教程

制作刷机包 打开‘yaffs2img浏览器’,点击左上角的‘选取yaffs2文件’选择你刚刚复制出来的 files文件夹里的system.img 先来认识一下这个软件 1.定制软件的提取(此部和制作刷机包没关系,可以不做,想用官方软件的同学可以 看看) 选择app,右键你想要提取软件,提取就可以了,我是把整个app文件夹提取出来了,不用 的软件直接删掉好了 2.定制软件的精简 在你不想要用的软件上直接右键,删除,就好了,你也可以右键添加你想要用的软件,得把

软件改成比较简短的英文名,否则有可能不能用 秀一下我精简后的列表,大家可以参照着精简 https://www.360docs.net/doc/f614711530.html,uncher文件的替换 下载好你想要用的桌面软件,改名为‘Launcher’,删掉app中的‘Launcher2’,添加进去你改好名字的‘Launcher’就好了,我比较喜欢ADW,所以我把ADW的文件名改为 Launcher,替换掉原来的Launcher2就好了 4.破音问题的解决 在左边导航点选‘etc’,右键添加文件,把附件中的声音配置文件解压出来 ‘AudioFilter.csv’添加进去就好了 AudioFilter.rar (355 Bytes)

5.字体的更改 下载字体文件,中文字体库一律把名字改名为‘DroidSans Fallback.ttf’,英文字体改为‘DroidSans.ttf ’,加粗的英文字体改为‘DroidSans-Bold.ttf ’然后再左边导航栏点选‘fonts’,把之前自带的字体删除,然后把你改好名字的字体添加进去就好了把国产机皇的字体也分享给大家,中文+英文+英文加粗 6.开机音乐和照相机音乐的删除 在导航栏点选‘media’,在audio/ui文件夹下,删除‘Bootsound.mp3’(开机音乐)和

第2天 linux系统的编译及镜像文件的制作

第2天linux系统的编译及镜像文件的制作 一般来说,linux系统分为几个映像。 一:bootload :一般常用的是 U-boot 二:内核映像:主要是linux内核编译成的映像比如TQ210开发板使用的zImage.bin 三:文件系统:有很多格式,比如 下面根据TQ210说明书进行讲解,大部分参考官方手册。以后自己修改源码可以在此基础上进行修改,修改完以后按照此步骤进行编译,编译完成后进行下载到开发板进行运行 1编译bootloader 1.1光盘中的 u-boot 源码的解压 先将光盘中的 u-boot 源码 ( 在光盘的“ TQ210_CD\bootloader\ ” 目录下 , 名为 uboot_TQ210_1.3.4_V1.1.tar.bz2)拷贝到 PC 的linux系统的根目录(这里说的根目录是本手册编写者的操 作和截图所拷贝的地方, 实际操作可以拷贝到任意目录下) 下, 然后使用命令#tar xvfjuboot_TQ210_1.3.4_V1.1.tar.bz2 -C /,解压源码,如下图所示 源码解压后,会在“/opt/EmbedSky/TQ210/uboot_TQ210_1.3.4/”目录下得到刚刚解压的源码。 1.2 光盘中的u-boot源码的编译 解压完成后,使用命令#make TQ210_config,配置u-boot,如下图所示:

使用命令#make,编译u-boot。编译结束后,在/opt/EmbedSky/TQ210/uboot_TQ210_1.3.4/目录下会得 到一个名字u-boot.bin的镜像,将其拷贝到Windows 或者拷贝到TFTP 服务器发送文件指定的目录中,就 可以烧写到开发板上面进行测试了(或者制作成SD 启动卡也可以测试)。如下两图所示:

S3C2440的Linux内核移植和yaffs2文件系统制作

L i n u x内核移植和根文件系统制作 第一章移植内核 (2) 1.1 Linux内核基础知识 (2) 1.1.1 Linux版本 (2) 1.1.2 什么是标准内核 (2) 1.1.3 Linux操作系统的分类 (3) 1.1.4 linux内核的选择 (4) 1.2 Linux内核启动过程概述 (5) 1.2.1 Bootloader启动过程 (5) 1.2.2 Linux启动过程 (7) 1.3 Linux内核移植 (10) 1.3.1 移植内核和根文件系统准备工作 (10) 1.3.2 修改Linux源码中参数 (11) 1.3.3 配置Linux内核 (15) 1.3.4、编译内核 (17) 第二章制作根文件系统 (19) 2.1 根文件系统预备知识 (19) 2.2、构建根文件按系统 (19) 2.2.1、建立根文件系统目录 (19) 2.2.2、建立动态链接库 (21) 2.2.3 交叉编译Bosybox (21) 2.2.4 建立etc目录下的配置文件 (24) 2.2.5 制作根文件系统映像文件 (26) 第三章启动系统 (27) 第四章总结 (34)

第一章移植内核 1.1 Linux内核基础知识 在动手进行Linux内核移植之前,非常有必要对Linux内核进行一定的了解,下面从Linux内核的版本和分类说起。 1.1.1 Linux版本 Linux内核的版本号可以从源代码的顶层目录下的Makefile中看到,比如2.6.29.1内核的Makefile中: VERSION = 2 PA TCHLEVEL = 6 SUBLEVEL = 29 EXTRA VERSION = .1 其中的“VERSION”和“PA TCHLEVEL”组成主版本号,比如 2.4、2.5、2.6等,稳定版本的德主版本号用偶数表示(比如2.6的内核),开发中的版本号用奇数表示(比如2.5),它是下一个稳定版本内核的前身。“SUBLEVEL”称为次版本号,它不分奇偶,顺序递增,每隔1~2个月发布一个稳定版本。“EXTRA VERSION”称为扩展版本号,它不分奇偶,顺序递增,每周发布几次扩展本版号。 1.1.2 什么是标准内核 按照资料上的习惯说法,标准内核(或称基础内核)就是指主要在https://www.360docs.net/doc/f614711530.html,/维护和获取的内核,实际上它也有平台属性的。这些linux 内核并不总是适用于所有linux支持的体系结构。实际上,这些内核版本很多时候并不是为一些流行的嵌入式linux系统开发的,也很少运行于这些嵌入式linux 系统上,这个站点上的内核首先确保的是在Intel X86体系结构上可以正常运行,它是基于X86处理器的内核,如对linux-2.4.18.tar.bz2的配置make menuconfig 时就可以看到,Processor type and features--->中只有386、486、586/K5/5x86/6x86/6x86MX、Pentium-Classic、Pentium-MMX、Pentium-Pro/Celeron/Pentium-II、Pentium-III/Celeron(Coppermine)、Pentium-4、K6/K6-II/K6-III 、Athlon/Duron/K7 、Elan 、Crusoe、Winchip-C6 、Winchip-2 、

(课程总结)

《嵌入式系统案例分析与设计》 课程实验报告 班级: 学号: 姓名: 指导老师: 成绩:

嵌入式系统案例分析与设计课程实验报告 一、开发环境的构建 1. 交叉工具链arm-linux-gcc安装与设置 (1)首先在家目录下创建一个文件夹,然后继续在该文件夹目录下创建一个test文件夹,如下图所示: (2)将交叉编译工具链Linux4.6.1拷到test目录下并解压 解压: tar -xvf arm-linux-gcc-4.5.1-v6-vfp-20120301.tgz (3)添加环境变量 PATH 决定了shell将到哪些目录中寻找命令或程序 配置编译环境路径在控制台下输入vim /etc/profile 在文件最后一行添加下面语句:export PATH=$PATH:/opt/FriendlyARM/toolschain/4.5.1/bin (4)保存关闭后,重启,用root账号重新登录系统,刚刚添加的环境变量生效,在控制台输入:arm-linux-gcc -v 如果安装成功将会输出arm-linux-gcc的版本号。如下图所示,显示gcc version 4.5.1 ,表示交叉编译工具链安装成功。

2. 设置TFTP服务器 (1)运行下面的命令,安装TFTP服务器和客户端# apt-get install tftpd-hpa tftp-hpa (2)运行下面的命令,重启TFTP服务器 # service tftpd-hpa restart

(3)新建一个文件,并将其移动到TFTP服务器的默认上传下载目录 (4)从服务器上下载test.txt文件,并退出tftp程序 (5)运行如下命令,确认下载的文件内容正确 3. 设置NFS服务器 (1)安装NFS服务器 # apt-get install nfs-kernel-server (2)运行下面的命令,创建一个目录,并在该目录下创建一个文件mkdir /nfs chown root /nfs chgrp root /nfs mkdir /nfs/rootfs echo "nfs test" > /nfs/rootfs/test.txt (3)编辑/ect/exports配置文件。 gedit /etc/exports

yaffs2制作教程

Yaffs2根文件系统制作 环境: 交叉编译环境:4.3.3 (天嵌科技提供,存放路径/opt/EmbedSky/4.3.3)开发平台:TQ2440 1,编译busybox 获取busybox源码 busybox-1.17.2.tar (https://www.360docs.net/doc/f614711530.html,/downloads/)置于目录/opt/embed下 #tar jxvf busybox-1.17.2.tar.bz2 #cd busybox-1.17.2 #vim Makefile 将164行改为CROSS_COMPILE = arm-linux- 将190行改为ARCH = arm 保存推出进入配置菜单 #make menuconfig 采用默认配置保存推出 #make #make install 在busybox-1.17.2的根目录下出现了一个_install目录在该目录下又有三个目录文件bin sbin usr 和一个链接文件 linuxrc 。 2,创建根文件系统必要的目录 回到/opt/embed目录下创建根文件系统必要的目录 #mkdir root_fs #cd root_fs 将刚才生成的三个目录bin sbin usr和一个链接文件linuxrc考到目录root_fs下

#cp -rf ../busybox-1.17.2/_install/* ./ #mkdir dev etc home lib mnt opt proc root sys tmp var 创建几个必要的二级目录 #mkdir usr/lib usr/share #mkdir etc/rc.d #mkdir var/lib var/lock var/run var/tmp 3,创建必要文件 (1), 获取库文件 (我的交叉编译工具链放在目录 /opt/EmbedSky/下的) #cp -rf /opt/EmbedSky/4.3.3/arm-none-linux-gn?i/libc/armv4t/lib/* so* lib -a (2),将主机 etc 目录下的passwd、group、shadow文件拷贝到 root_fs/etc 目录下 #cp -f /etc/passwd /etc/group /etc/shadow etc 将目录/opt/embed/busybox-1.17.2/examples/bootfloppy/etc下的所有文件拷贝到 root_fs/etc下。在这个目录下有三个文件fstab, inittab, profile和一个目录init.d, 在目录init.d中有一个文件rcS。 #cp -rf ../busybox-1.17.2/examples/bootfloppy/etc/* etc 在目录etc下创建文件mdev.conf。mdev是?v的一个简化版本,我们可以通过文件mdev.conf自定义一些设备节点的名称或链接来满足特定的需要,但在此处让它为空。 #touch etc/mdev.conf (3)创建两个设备文件dev/console dev/null。 在linux内核源码文件init/main.c中有打开设备文件dev/console 的操作如下:

Android 2.1 源码结构分析

Android 2.1 |-- Makefile |-- bionic (bionic C库) |-- bootable (启动引导相关代码) |-- build (存放系统编译规则及generic等基础开发包配置) |-- cts (Android兼容性测试套件标准) |-- dalvik (dalvik JAVA虚拟机) |-- development (应用程序开发相关) |-- external (android使用的一些开源的模组) |-- frameworks (核心框架——java及C++语言) |-- hardware (主要保护硬解适配层HAL代码) |-- out (编译完成后的代码输出与此目录) |-- packages (应用程序包) |-- prebuilt (x86和arm架构下预编译的一些资源) |-- sdk (sdk及模拟器) |-- system (文件系统库、应用及组件——C语言) `-- vendor (厂商定制代码) bionic 目录 |-- libc (C库) | |-- arch-arm (ARM架构,包含系统调用汇编实现) | |-- arch-x86 (x86架构,包含系统调用汇编实现) | |-- bionic (由C实现的功能,架构无关) | |-- docs (文档) | |-- include (头文件) | |-- inet (?inet相关,具体作用不明) | |-- kernel (Linux内核中的一些头文件) | |-- netbsd (?nesbsd系统相关,具体作用不明) | |-- private (?一些私有的头文件) | |-- stdio (stdio实现) | |-- stdlib (stdlib实现) | |-- string (string函数实现) | |-- tools (几个工具) | |-- tzcode (时区相关代码) | |-- unistd (unistd实现) | `-- zoneinfo (时区信息) |-- libdl (libdl实现,dl是动态链接,提供访问动态链接库的功能)|-- libm (libm数学库的实现,) | |-- alpha (apaha架构) | |-- amd64 (amd64架构) | |-- arm (arm架构) | |-- bsdsrc (?bsd的源码) | |-- i386 (i386架构) | |-- i387 (i387架构?)

《嵌入式操作系统》课程设计(DOC)

《嵌入式操作系统》课程设计指导书 专业:计算机科学与技术专业方向:计算机科学与技术 计算机科学与工程学院

第一章课程设计指导书 1、目的任务 本设计的目的在于使学生全面理解实践已学的相关课程内容,深刻理解嵌入式系统开发的全过程。从硬件的角度掌握嵌入式开发板的组成,接口部件的结构和与宿主机间的连接,从软件的角度掌握嵌入式系统软件开发的全过程。使学生通过自己的实践,初步了解和掌握一个实用嵌入式系统的开发步骤,综合应用所学的基础知识和编程手段独立完成嵌入式系统开发的基础内容。 2、设计内容 基础实验(必做)——嵌入式系统开发环境搭建 扩展实验(从以下三个实验中任选一个) 实验一——利用Autotools工具自动生成Makefile文件 实验二——使用BusyBox制作根文件系统 实验三——使用GTK+进行图形界面编程 3、时间安排 时间:第17周 第一天确定题目、查找相关资料,安装系统;第二到第四天为实验和程序设计、完善总结、撰写报告,第五天答辩。 4、工作要求 (1)爱护实验开发板,为避免烧坏开发板,对开发板的任何插拔工作,都必须在断电之后进行。 (2)明确实验要求和步骤,在进行实验之前详细阅读开发板配套手册和相关资料。

(3)在本设计过程中,学生应随时做实习笔记,记录每天的工作内容及结果,同时还应规划出次日的实习计划与解决问题的方案。 5、成绩评定 设计结束时,由指导老师对学生进行全面考核,评分按五级分制(优、良、中、及格、不及格)评定成绩,评分依据以下几个方面。 (1)平时成绩 包括遵守纪律情况,实习中的工作态度,实习日记的记录情况等。 (2)设计中实验结果的成绩 教师要把关,确认实验结果是由实验人做出的。如不能按设计要求做出实验结果的,要予以扣分;对于有创新的实验及结果者,应给予好的成绩。对于以下情况之一者,要严格进行处理。 ①. 照抄他人,自己没有消化者,应给予“不及格”。 ②. 严重迟到早退,应给予“不及格”。 ③. 实习不认真,违反实验室规定者,应给予“不及格”。 ④. 缺勤大于整个工作时间的25%以上者,应给予“不及格”。 ⑤. 实习报告不认真者,至少不能给“优”和“良”的成绩。 6、参考资料 (1)熊茂华、熊昕编著.嵌入式Linux实时操作系统及应用编程.清华大学出版社.2011年5月第1版 (2)深圳友坚恒天科技公司开发板配套光盘中用户手册: idea6410开发板linux使用手册 ubuntu-UserManual_v0.18 UT6410-Android2.1_manual (3)相关国嵌、申嵌视频资料

《嵌入式操作系统》实验报告

《嵌入式操作系统》实验报告 班级计算机 学号 姓名 指导教师庄旭菲

工业大学信息工程学院计算机系 2018年6月 实验一 Linux核移植与编译实验 1. 实验目的 ?了解Linux 核相关知识与核结构 ?了解Linux 核在ARM 设备上移植的基本步骤和方法 ?掌握Linux 核裁剪与定制的基本方法 2. 实验容 ?分析Linux 核的基本结构,了解Linux 核在ARM 设备上移植的一些基本步骤及常识。 ?学习Linux 核裁剪定制的基本配置方法,利用UP-Magic210 型设备配套Linux 核进行 自定义功能(如helloworld 显示)的添加,并重新编译核源码,生成核压缩文件zImage,下载到UP-Magic210 型设备中测试。 3. 实验步骤

实验目录:/UP-Magic210/SRC/kernel/linux-2.6.35.7/ 编译核:在宿主机端为UP-Magic210 设备的Linux 2.6.35.7 核编写简单的测试驱动(核)程序helloworld.c 并修改核目录中相关文件,添加对测试驱动程序的支持。 (1)、使用vim 编辑器手动编写实验代码helloworld.c helloworld.c 如如下: #include #include MODULE_LICENSE("Dual BSD/GPL"); //驱动程序入口函数 static int hello_init(void) { printk(KERN_ALERT "##############Hello, world############\n"); return 0; } //驱动程序出口函数 static void hello_exit(void) { printk(KERN_ALERT "###############Goodbye, world#########\n"); } module_init(hello_init); module_exit(hello_exit);

YAFFS2启动坏块问题

YAFFS2启动坏块问题 动的时候出现一大串: block 2 is bad block 3 is bad block 4 is bad block 5 is bad block 6 is bad block 7 is bad block 8 is bad block 9 is bad ... VFS:Mounted root yaffs filesystem. Freeing init memory: 120K Warning: unable to open an initial console. Failed to execute /linuxrc. Attempting defaults... Kernel panic - not syncing: No init found. Try passing init option to kernel. 或者block 2 is bad 没有出现直接显示 VFS: Mounted root yaffs filesystem. Freeing init memory: 120K Warning: unable to open an initial console. Failed to execute /linuxrc. Attempting defaults... Kernel panic - not syncing: No init found. Try passing init option to kernel. 其实都是 MTD没有正确地读取到 root 可以在 u-boot 中执行 nand bad 查看 bad block 的信息如果从0x200000 - 0x1200000 之间有很多bad block说明nand write.yaffs 这个命令还有问题 OOB 的信息没有正确地写入在 NANDFLASH 只写入了 data 而 OOB 没有写进去. 下面说说如果解决这个问题:在 NANDFLASH 的写入过程中其中会调用 nand_do_write_ops 在这个函数中 if unlikelyoob oob nand_fill_oobchip oob ops 这两行要注意在这两行之前 OOB buf一直都是正解的但这两行之后 OOB 的数据就变成了 0xFF了在靠前的一些 block OOB 都是 0xFF但是到了后面 OOB的数据就变得杂乱无章了所以就会出现一大串的 bad flash为什么会变成没有规律原因还没有找到将这两行MASK改为memcpychip-oob_poi oob ops-ooblen 问题就解决了或者在nand_write_opts 函数中将 oob_ops.mode MTD_OOB_AUTO 改为 oob_ops.mode MTD_OOB_RAW 问题同样可以解决.

Yaffs功能说明

? 1 背景 ? 2 发牌 ? 3 Yaffs 和 Yaffs 直接接口有哪些? ? 4 为什么使用 Yaffs 吗? ? 5 源代码和 Yaffs 资源 ? 6 系统要求 ?7 如何与实时操作系统/Embedded 系统集成 Yaffs ?7.1 源文件 ?7.2 整合POSIX应用程序接口 ?7.3 RTO集成接口 ?8 Yaffs NAND模型 ?Yaffs1 8.1 NAND模型注意事项 ?Yaffs2 8.2 NAND模型 ?9 NAND配置和访问接口 ?9.1 常见配置项 (Yaffs1 和 Yaffs2) ?9.2 共同访问函数 (Yaffs1 和 Yaffs2) ?9.3 Yaffs1 访问函数 ?9.4 Yaffs2 访问函数 ?10 使用POSIX文件系统接口 ?10.1 向 Windows POSIX差异-喜欢接口 ?10.2 基本概念 ?10.3 错误代码 ?10.4 链接——硬种 (不是符号链接) ?10.5 符号链接 ?10.6 基于句柄的文件处理 ?10.7 更改文件大小 ?10.8 获取/设置有关文件的信息 ?10.9 更改目录结构和名称 ?10.10 搜索目录 ?10.11 装载控制 ?10.12 其他 ?11 示例: yaffs_readdir() 和 yaffs_stat() 1 背景 本文档的目的是描述接口的Yaffs 直接接口(YDI) 以及提供足够的信息,以允许Yaffs 初步评价。这份文件试图把重点放在重要的系统集成商的问题没有得到Yaffs 是如何工作的太详细。其他文件提供Yaffs 的工作原理进行深入的讨论。 2 发牌

yaffs2文件系统分析

yaffs2文件系统分析 作者:dreamice 1.前言 略。 2.yaffs文件系统简介 按理说这里应该出现一些诸如“yaffs是一种适合于NAND Flash的文件系统XXXXX”之类的字眼,不过考虑到网络上关于yaffs/yaffs2的介绍已经多如牛毛,所以同上,略。 3.本文内容组织 本文将模仿《linux内核源代码情景分析》一书,以情景分析的方式对yaffs2文件系统的 源代码进行分析。首先将分析几组底层函数,如存储空间的分配和释放等;其次分析文件逻辑地址映射;然后是垃圾收集机制;接下来……Sorry,本人还没想好。:-) 4.说明 因为yaffs2貌似还在持续更新中,所以本文所列代码可能和读者手中的代码不完全一致。另外,本文读者应熟悉C语言,熟悉NAND Flash的基本概念(如block和page)。 Ok,步入正题。首先分析存储空间的分配。 5.NAND Flash存储空间分配和释放 我们知道,NAND Flash的基本擦除单位是Block,而基本写入单位是page。yaffs2在分配存储空间的时候是以page为单位的,不过在yaffs2中把基本存储单位称为chunk,和page是一样的大小,在大多数情况下和page是一个意思。在下文中我们使用chunk这个词,以保持和yaffs2的源代码一致。 我们先看存储空间的分配(在yaffs_guts.c中。这个文件也是yaffs2文件系统的核心部分):Yaffs2中将该函数更名为yaffs_alloc_chunk。 static int yaffs_AllocateChunk(yaffs_Device * dev, int useReserve, yaffs_BlockInfo **blockUsedPtr) { int retVal; yaffs_BlockInfo *bi; if (dev->allocationBlock < 0) { /* Get next block to allocate off */ dev->allocationBlock = yaffs_FindBlockForAllocation(dev); dev->allocationPage = 0; } 函数有三个参数,dev是yaffs_Device结构的指针,yaffs2用这个结构来记录一个NAND 器件的属性(如block和page的大小)和系统运行过程中的一些统计值(如器件中可用chunk 的总数),还用这个结构维护着一组NAND操作函数(如读、写、删除)的指针。 整个结构体比较大,我们会按情景的不同分别分析。useReserve表示是否使用保留空间。yaffs2文件系统并不会将所有的存储空间全部用于存储文件系统数据,而要空出部分block用于垃圾收集时使用。一般情况下这个参数都是0,只有在垃圾收集时需要分配存 储空间的情况下将该参数置1。yaffs_BlockInfo 是描述block属性的结构,主要由一些统计变量组成,比如该block内还剩多少空闲page等。我们同样在具体情景中再分析这个结构中的字段含义。 函数首先判断dev->allocationBlock的值是否小于0。yaffs_Device结构内的allocationBlock字段用于记录当前从中分配chunk(page)的那个block的序号。当一 个block内的所有page全部分配完毕时,就将这个字段置为-1,下次进入该函数时就会

深入理解yaffs2文件系统(一)

深入理解yaffs2文件系统(一) 1、Flash文件系统 1.1、背景 已经有多种flash文件系统(FFSs)或flash块驱动(在之上运行一个常规的FS),同时都有优点或缺点。 Flash存储器有非常多的限制,这里就不一一列举了。已经有各种方法解决这些限制,以提供一个文件系统。必须认识到,“flash”,包括NOR和NAND,各自有不同的限制。很容易被专业术语“flash”误导,误以为用于NorFlash的方法也立即适用于NandFlash。 Nand块驱动一般采用FAT16作为文件系统,但不够健壮,也不够贴近Flash的特性。这些块驱动通过一个“本地--物理”的映射层来仿真可写的、类似于磁盘扇区的块。当使用FAT16时,这些文件系统工作的相当好,它们内存消耗小,代码尺寸也很小。但就像所有基于FAT 的系统一样,它们很容易损坏(如,丢失簇)。 其他的途径则是设计整个文件系统,不是基于块驱动,而且是flash友好的,这允许更多的余地来解决上述所提到的问题。 当前有两个linux文件系统能非常好的支持NorFLash,那就是JFFS以及它的升级版本JFFS2。这两者都提供日志机制,大大的提升了健壮性,这也是嵌入式系统特别重要的一个特性。不幸的是,它们在RAM消耗和启动时间方面都不是很好。 JFFS在flash中的每一个journalling日志节点,需要一个基于RAM的jffs_node结构,每一个节点为48字节。JFFS2做了一个大改进,通过剪裁相关的结构体(jffs2_raw_node_ref)而减少到16字节。即使如此,在512字节页大小128M的NandFlash,按平均节点大小来算,也需要250000字节约4M大小。 JFFS和JFFS2在启动时,需要扫描整个flash阵列来查找journaling节点,并决定文件结构。由于NAND容量大、慢、连续访问、需要ECC校验,这些特性将导致不可接受的、很长的启动时间。随便掐指一算,扫描128M字节的Nand阵列大小需要25秒钟。 设计yaffs2的目的就是:NandFlash友好的、通过提供日志机制达到健壮的、大大减少JFFSx 所具有的RAM消耗和启动时间。Yaffs主要是用于内部Nand而不是可移动的Nand(SM卡)。在可移动的SM智能卡,兼容性显得更重要,一般使用FAT文件系统。当然,yaffs也做了深思熟虑,认为稳定性比兼容性更重要。 1.2、Yaffs文件系统特性 YAFFS是一个专为NandFlash特性设计的文件系统。它已经被证实的好特性有: (1)fast –快速,比其他Flash文件系统要快很多。 (2)Easily ported –易于移植,已经移植到GNU/Linux,WinCE,eCOS,pSOS,VxWorks,以及其他各种系统。 (3)Log structured –日志结构,提供均衡负载,使得它非常健壮。 (4)支持多种类型的NandFlash芯片,如页大小为512B、1KB、2KB的NnadFlash等等。(5)Very fast mount –非常快速的文件系统挂载速度,几乎是立即启动的。 (6)非常少的RAM消耗。 (7)灵活的Licensing授权机制,适合许多情况。 YAFFS当前版本为v2,yaffs2除了支持512字节页大小的flash,还支持2K字节页大小的flash (YAFFS1仅仅支持原先的512字节页大小的flash)。YAFFS 1 和2已经被众多的商业产品所采用。 2、关于yaffs1文件系统

相关主题
相关文档
最新文档