先来回顾一个潜在的结构 在skb的 pskb_copy, skb_copy 等操作中起到了关键作用
写道
struct skb_shared_info {
unsigned short nr_frags;
unsigned short gso_size;
/* Warning: this field is not always filled in (UFO)! */
unsigned short gso_segs;
unsigned short gso_type;
__be32 ip6_frag_id;
__u8 tx_flags;
struct sk_buff *frag_list;
struct skb_shared_hwtstamps hwtstamps;
atomic_t dataref;
void * destructor_arg;
skb_frag_t frags[MAX_SKB_FRAGS];
};
想要顺利的访问这个结构就要了解一个宏
#define skb_shinfo(SKB) ((struct skb_shared_info *)((SKB)->end))
再回忆一下头部结构
写道
struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 ihl:4,
version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
__u8 version:4,
ihl:4;
#else
#error "Please fix <asm/byteorder.h>"
#endif
__u8 tos;
__be16 tot_len;
__be16 id;
__be16 frag_off;
__u8 ttl;
__u8 protocol;
__sum16 check;
__be32 saddr;
__be32 daddr;
/*The options start here. */
};
好了现在就看看, 上前天说到的 ip_rcv 如何Ip处理包
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
{
/*在sk_buff __netif_receive_skb eth_type_trans 中已经让包符合L3 */
struct iphdr *iph;
u32 len;
/*如果是因为开了promiscuous 模式而让垃圾包到了L3 ,就丢弃不属于自己的 */
if (skb->pkt_type == PACKET_OTHERHOST)
goto drop;
/*依旧SNMP 采集点*/
IP_UPD_PO_STATS_BH(dev_net(dev), IPSTATS_MIB_IN, skb->len);
/*如果这包在别的子系统也使用 就拷贝一份给自己专门用*/
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS);
goto out;/*拷贝失败就丢弃*/
}
/*如果是分包的话 ,用__pskb_pull_tail把skb_shared_info中frag_list找回来 */
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
goto inhdr_error;
/*重新指向L3头部*/
iph = ip_hdr(skb);
/*
* RFC1122: 3.2.1.2 MUST silently discard any IP frame that fails the checksum.
*
* Is the datagram acceptable?
*
* 1. Length at least the size of an ip header
* 2. Version of 4
* 3. Checksums correctly. [Speed optimisation for later, skip loopback checksums]
* 4. Doesn't have a bogus length
*/
/*头部和版本检查*/
if (iph->ihl < 5 || iph->version != 4)
goto inhdr_error;
/*同样的动作, 不过和上一次比这次是整个IP头部了*/
if (!pskb_may_pull(skb, iph->ihl*4))
goto inhdr_error;
iph = ip_hdr(skb);
/*校验*/
if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
goto inhdr_error;
/*长度完整性校验*/
len = ntohs(iph->tot_len);
/*skb buffer的真实长度只能比包头报告的长度大<因为可能被L2层填充了>或正好,小的话就有问题咯*/
if (skb->len < len) {
IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INTRUNCATEDPKTS);
goto drop;
} else if (len < (iph->ihl*4))
/*包头可能比包体还长吗? :)*/
goto inhdr_error;
/* Our transport medium may have padded the buffer out. Now we know it
* is IP we can trim to the true length of the frame.
* Note this now means skb->len holds ntohs(iph->tot_len).
*/
/*被L2填充了吗? 去掉! 前面校验不算~*/
if (pskb_trim_rcsum(skb, len)) {
IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS);
goto drop;
}
/* Remove any debris in the socket control block */
/*看起来L3不需要 ip_options*/
memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
/* Must drop socket now because of tproxy. */
/*既然都被我处理过了,就跟我把,帮你净身*/
skb_orphan(skb);
/*接受Netfilter 的洗礼吧(LVS基于此),最后再执行 ip_rcv_finish,这是正常之旅*/
return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, dev, NULL,
ip_rcv_finish);
inhdr_error:
/*去MIB树上反应一下*/
IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INHDRERRORS);
drop:
kfree_skb(skb);
out: /*哎*/
return NET_RX_DROP;
}
接下来就是看看 ip_rcv_finish
static int ip_rcv_finish(struct sk_buff *skb)
{
const struct iphdr *iph = ip_hdr(skb);
struct rtable *rt;
/*
* 如果包中没有dst_entry结构(不知如何转发),就直接询问路由子系统看看有没人要它(后面就不处理了)
*/
if (skb_dst(skb) == NULL) {
int err = ip_route_input_noref(skb, iph->daddr, iph->saddr,
iph->tos, skb->dev);
if (unlikely(err)) {
if (err == -EHOSTUNREACH)
IP_INC_STATS_BH(dev_net(skb->dev),
IPSTATS_MIB_INADDRERRORS);
else if (err == -ENETUNREACH)
IP_INC_STATS_BH(dev_net(skb->dev),
IPSTATS_MIB_INNOROUTES);
else if (err == -EXDEV)
NET_INC_STATS_BH(dev_net(skb->dev),
LINUX_MIB_IPRPFILTER);
goto drop;
}
}
/*更新该CPU的ip_rt_acct 统计,参考/proc/net/rt_acct*/
#ifdef CONFIG_NET_CLS_ROUTE
if (unlikely(skb_dst(skb)->tclassid)) {
struct ip_rt_acct *st = this_cpu_ptr(ip_rt_acct);
u32 idx = skb_dst(skb)->tclassid;
st[idx&0xFF].o_packets++;
st[idx&0xFF].o_bytes += skb->len;
st[(idx>>16)&0xFF].i_packets++;
st[(idx>>16)&0xFF].i_bytes += skb->len;
}
#endif
/*如果有IP options就去处理*/
if (iph->ihl > 5 && ip_rcv_options(skb))
goto drop;
/*根据目的地类型做一些统计*/
rt = skb_rtable(skb);
if (rt->rt_type == RTN_MULTICAST) {
IP_UPD_PO_STATS_BH(dev_net(rt->dst.dev), IPSTATS_MIB_INMCAST,
skb->len);
} else if (rt->rt_type == RTN_BROADCAST)
IP_UPD_PO_STATS_BH(dev_net(rt->dst.dev), IPSTATS_MIB_INBCAST,
skb->len);
/"where to go 见下面"/
return dst_input(skb);
drop:
kfree_skb(skb);
return NET_RX_DROP;
}
这个决定L3包走向的函数指针在那里设置的呢,跟着我看一下吧
接着上一篇讲过的inet_init()->ip_init()->ip_rt_init()
-->
rtnl_register(PF_INET, RTM_GETROUTE, inet_rtm_getroute, NULL);
<route.c __rtnl_register登记 >
-->
int ip_route_input_common(struct sk_buff *skb, __be32 daddr, __be32 saddr,
u8 tos, struct net_device *dev, bool noref)
{
//...
if (our ) {
int res = ip_route_input_mc(skb, daddr, saddr,
tos, dev, our);
//...
}
//...
}
//...
res = ip_route_input_slow(skb, daddr, saddr, tos, dev);
}
inet_rtm_getroute()->ip_route_input()->ip_route_input_common()-
A:ip_route_input_slow()
->ip_mkroute_input()->__mkroute_input()
"ip_forward;"
B<our>: ip_route_input_mc()
B1 "ip_local_deliver"
B2 "init_net.loopback_dev";
这是几个likely的不考虑异常分支,别的就先不介绍了
int ip_forward(struct sk_buff *skb)
{
struct iphdr *iph; /* Our header */
struct rtable *rt; /* Route we use */
struct ip_options * opt = &(IPCB(skb)->opt);
if (skb_warn_if_lro(skb))
goto drop;
if (!xfrm4_policy_check(NULL, XFRM_POLICY_FWD, skb))
goto drop;
/*Router Alert option 的处理,后面会分析 ip_call_ra_chain()函数*/
if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))
return NET_RX_SUCCESS;
/*pkt_type在L2层处理的时候有无设置PACKET_HOST*/
if (skb->pkt_type != PACKET_HOST)
goto drop;
/*什么都没做 直接让pass了*/
skb_forward_csum(skb);
/*rfc 规定防止路由循环等等用的*/
if (ip_hdr(skb)->ttl <= 1)
goto too_many_hops;
/*VPN IPSec交给xfrm 框架处理转发*/
if (!xfrm4_route_forward(skb))
goto drop;
rt = skb_rtable(skb);
/*如果IP包选项指明了要用自己提供的路由来走<Strict Source Routing >,而又不能满*足就失败*/
if (opt->is_strictroute && rt->rt_dst != rt->rt_gateway)
goto sr_failed;
/*如果包长度大于了目的地的MTU 却禁止分包 就发送一个ICMP<这块参考 TCP/IP详解卷一 9章>*/
if (unlikely(skb->len > dst_mtu(&rt->dst) && !skb_is_gso(skb) &&
(ip_hdr(skb)->frag_off & htons(IP_DF))) && !skb->local_df) {
IP_INC_STATS(dev_net(rt->dst.dev), IPSTATS_MIB_FRAGFAILS);
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
htonl(dst_mtu(&rt->dst)));
goto drop;
}
/* We are about to mangle packet. Copy it! 这个之前解释过了 */
if (skb_cow(skb, LL_RESERVED_SPACE(rt->dst.dev)+rt->dst.header_len))
goto drop;
iph = ip_hdr(skb);
/* Decrease ttl after skb cow done 协议要求*/
ip_decrease_ttl(iph);
/*
*如果包允许走别的路由而且他也表示希望走一个更好的,就重新计算路由,当然也会 * 引起一个ICMP ,这个函数后面分析 */
if (rt->rt_flags&RTCF_DOREDIRECT && !opt->srr && !skb_sec_path(skb))
ip_rt_send_redirect(skb);
/*根据 IP包头部的TOS 设置包优先级 给后面的 Traffic Control 用<ps: 路由器设计很重视这个选项>*/
skb->priority = rt_tos2priority(iph->tos);
/*之前分析过,经过NetFilter后调用ip_forward_finish*/
return NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, skb, skb->dev,
rt->dst.dev, ip_forward_finish);
sr_failed:
/*
* Strict routing permits no gatewaying 协议规定的参考 TCP/IP-I 1
*/
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0);
goto drop;
too_many_hops:
/* Tell the sender its packet died... */
IP_INC_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_INHDRERRORS);
icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0);
drop:
kfree_skb(skb);
return NET_RX_DROP;
}
好吧 ,下面就是
static int ip_forward_finish(struct sk_buff *skb)
{
struct ip_options * opt = &(IPCB(skb)->opt);
IP_INC_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_OUTFORWDATAGRAMS);
/* ip_forward里面已经处理了2个IP options <Router Alert 和Strict Source Routing>,* 下面就是把剩下的处理完,由于几乎很少会用到IP-O <看看这里的unlikely也知道概率了吧* >所以就不分析了*/
if (unlikely(opt->optlen))
ip_forward_options(skb);
/*最终归属,根据 消息包类型去调用相应的函数指针 ip_mc_output ..ip_finish_output */
return dst_output(skb);
}
=========================
补充上面说的 ip_call_ra_chian
写道
struct ip_ra_chain {
struct ip_ra_chain __rcu *next;
struct sock *sk;
union {
void (*destructor)(struct sock *);
struct sock *saved_sk;
};
struct rcu_head rcu;
};
int ip_call_ra_chain(struct sk_buff *skb)
{
struct ip_ra_chain *ra;
u8 protocol = ip_hdr(skb)->protocol;
struct sock *last = NULL;
struct net_device *dev = skb->dev;
/*这里遍历了整个raw sock 链表 */
for (ra = rcu_dereference(ip_ra_chain); ra; ra = rcu_dereference(ra->next)) {
struct sock *sk = ra->sk;
/* If socket is bound to an interface, only report
* the packet if it came from that interface.
*/
/*包头端口号和该raw sock 的端口匹配 设备接口序号也匹配*/
if (sk && inet_sk(sk)->inet_num == protocol &&
(!sk->sk_bound_dev_if ||
sk->sk_bound_dev_if == dev->ifindex) &&
net_eq(sock_net(sk), dev_net(dev))) {
/*如果分段过 就去重组整个IP包 ip_fragment.c*/
if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) {
if (ip_defrag(skb, IP_DEFRAG_CALL_RA_CHAIN))
return 1;
}
if (last) {
/*关键就是这里,把包复制一遍然后传给上层
*放入该sock的sk_receive_queue*/
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
if (skb2)
raw_rcv(last, skb2);
}
/*下一个raw sock */
last = sk;
}
}
if (last) {
raw_rcv(last, skb);
return 1;
}
分享到:
相关推荐
原创的友善之臂的mini6410 linux-2.6.38内核的uart1 串口驱动,使用platform_device方式,压缩包里面有驱动源代码、编译好了的ko文件、使用说明文档、用户例程的源代码和可执行程序,但是并没有给出直接编译驱动和...
Linux内核2.6.38是Linux操作系统的核心部分,负责管理系统的硬件资源,调度进程,实现硬件抽象层,提供系统调用接口等关键功能。对于开发者而言,它是一个极其重要的基础,尤其对于那些需要定制化操作系统的专业人员...
Mini6410 Kernel-patch Linux-2.6.38
在Ubuntu 10.10环境下编译Linux内核2.6.38涉及一系列步骤,需要对操作系统、内核版本、编译工具和配置选项有深入理解。以下是详细的编译流程和注意事项: 首先,确保你的环境是Pentium 4架构的Ubuntu 10.10(内核...
ubuntu编译ARM内核linux-2.6.38所需的工具
### 基于 Linux-2.6.38 内核的嵌入式驱动常用函数调用解析 #### 一、`copy_from_user()` **函数定义:** ```c static inline unsigned long copy_from_user(void __user *to, const void *from, unsigned long n)...
在本文中,我们将深入探讨如何将CS8900A网络控制器从Linux 2.6.35内核移植到2.6.38内核的过程。CS8900A是一款广泛使用的以太网控制器,尤其适用于嵌入式系统。在基于FS2410的开发板上进行移植工作,我们需要理解内核...
最新的Linux2.6操作系统,Linux-2.6.38.4,加入了新的功能,下载体验吧
### Linux 2.6.38.8 NAND 与 YAFFS2 移植详解 #### 前言 在嵌入式系统开发过程中,针对特定硬件平台进行内核及文件系统的移植是一项基本且重要的任务。本文将详细介绍如何在 Linux 2.6.38.8 内核上移植 NAND Flash...
Linux 2.6.38内核是Linux操作系统的一个版本,它包含了多种驱动开发所需的函数和API。在开发基于Linux内核的设备驱动程序时,熟练使用这些内核提供的函数是至关重要的。下面将详细介绍文档中提及的一些内核驱动常用...
v2.6.38 v2.6.39 v3.0.1 v3.0.11 v3.0.51 v3.1.1 v3.2.1 v3.2.51 v3.3.1 v3.4.1 v3.4.11 v3.4.51 v3.4.111 v3.5.1 v3.6.1 v3.7.1 v3.8.1 v3.8.11 v3.9.1 v3.9.11 v3.10.1 v3.10.11 v3.10.51 v3.11.1 v3.12.1 v3.12....
在本文中,我们将深入探讨如何在Tiny6410开发板上移植Linux内核2.6.38。Tiny6410是一款基于ARM Cortex-A8处理器的嵌入式开发板,具有2GB NAND Flash存储和256MB RAM。移植内核到这样的硬件平台涉及多个步骤,包括...
综上所述,"arm_linux-2.6.21_s3c2440.rar"这个压缩包很可能包含了一套针对S3C2440处理器的Linux内核配置、编译和驱动程序,用于在ARM平台上实现Linux操作系统的运行。这些资源对于开发者来说是宝贵的,他们可以利用...
对2.6.38内核中wifi驱动中出现的SD卡热插拔,wifi驱动进行了一个初步的分析.有什么不对的地方,还希望高手指点!
### 6410-Linux驱动程序的位置及详细介绍 #### 一、概述 本文档旨在为Mini6410和Tiny6410开发板的用户介绍Linux系统中各种驱动程序的具体位置及其所对应的设备名。这两款开发板在硬件资源分配上保持了一致性,因此...
erver01> tar xvf linux-2.6.38.tar.bz2 server01> cd linux-2.6.38 server01> make mrproper server01> cp ../linux-2.6.18-xxx/.config .config #此处为了防止遗漏某些模块的添加,将原来的内核配置文件拷贝过来...
### CS8900网卡驱动在Linux Kernel 2.6.38中的移植与调试 #### 一、概述 本文旨在详细介绍如何将原本适用于Linux Kernel 2.6.27及以下版本的CS8900网卡驱动移植到更高版本的Linux Kernel 2.6.38上。由于内核版本的...
* #include <linux/miscdevice.h> * #include <linux/delay.h> * #include <asm/irq.h> * #include <mach/regs-gpio.h> * #include <mach/hardware.h> * #include <linux/kernel.h> * #include <linux/module.h> 本...
TQ2440的u_boot不能引导2.6.36以上的内核,用TQ2440的u_boot修改的,可以引导linux2.6.36以上的内核,同时兼容linux2.6.37以下老版本内核 用法: #make distclean #make Wamy2440_config #make 下载u-boot.bin后...
在Linux 2.6.38版本中,驱动程序设计涉及到多个关键的结构体,包括`struct file_operations`, `struct inode`, 和 `struct file`。下面是对这些结构体及其关系的详细解释。 1. **struct file_operations** 这个...