大地资源网视频在线观看新浪,日本春药精油按摩系列,成人av骚妻潮喷,国产xxxx搡xxxxx搡麻豆

您現在的位置:智能制造網>技術中心>Linux的TCP/IP協議棧閱讀筆記

直播推薦

更多>

企業動態

更多>

推薦展會

更多>

Linux的TCP/IP協議棧閱讀筆記

2009年03月09日 19:00:10人氣:4080來源:浙江啟揚智能科技有限公司


下面是我看RH6.2 Kernel 2-2-14的TCP/IP代碼的筆記
當然我水平比較低,也沒有整理,自己也有很多不明白的地方,
主要是邊看邊貼,大家有什么問題一起討論把,特別是scz,
你也真狠呀:(

以下的文章假定你有TCP/IP的知識,看過TCPv1或者Comer的Vol1
然后編過一些Linux下面的socket程序,好,Come on

Linux內核的啟動我就不說了,反正我的匯編也快忘光了,也沒有
學習過GAS。先從init/main.c的start_kernel函數說起。
在這個函數里面調用kernel_thread啟動了init進程,這個進程對應
的函數是同一個文件里面的init函數,在init函數里面調用了一個
叫do_basic_setup的在同一個文件里面的函數,這個函數調用了
net/socket.c里面的sock_init函數,這個函數就是TCP/IP協議棧,
也包括ipx等的入口。

首先sock_init函數里面有很多ifdef這樣的東東,我覺得對于一個
普通的主機來說,這些都不會配置的,它們包括:
SLAB_SKB,CONFIG_WAN_ROUTER,CONFIG_FIREWALL,CONFIG_RTNETLINK,
CONFIG_NETLINK_DEV
不過說實話除了CONFIG_WAN_ROUTER和CONFIG_FIREWALL可以看出來
是什么意思以外,其它的我都是因為沒有見過所以認為不會配置的,
有沒有高手出來指點一下呀?:(

去掉了這些編譯選項以后就剩下這樣的代碼:
for (i = 0; i < NPROTO; i++)
net_families[i] = NULL;
sk_init();
proto_init();

其中net_families在include/linux/net.h里面定義,是這樣的:
struct net_proto_family
{
int family;
int (*create)(struct socket *sock, int protocol);
/* These are counters for the number of different methods of
each we support */
short authentication;
short encryption;
short encrypt_net;
};
其中有用的只有前兩項,那個create的callback函數是每個協議,例如
AF_INET等初始化上層協議如TCP/ICMP協議需要的,以后還會遇到的,這
里先放著把

sk_init函數在net/core/sock.c里面,沒什么說的..
struct sock *sk_alloc(int family, int priority, int zero_it)
{
struct sock *sk = kmem_cache_alloc(sk_cachep, priority);
if(sk) {
if (zero_it)
memset(sk, 0, sizeof(struct sock));
sk->family = family;
}
return sk;
}

proto_init函數在同一個文件里面:
void __init proto_init(void)
{
extern struct net_proto protocols[];
struct net_proto *pro;
pro = protocols;
while (pro->name != NULL)
{
(*pro->init_func)(pro);
pro++;
}
}

struct net_proto在include/linux/net.h里面是這樣的:
struct net_proto
{
const *name; /* Protocol name */
void (*init_func)(struct net_proto *); /* Bootstrap */
};
這個protocols的數組是在net/protocols.c里面定義的,包含了
一堆的協議初始化結構體,其中我只注意兩個:AF_INET和AF_PACKET
它們的初始化函數分別是inet_proto_init和packet_proto_init
(待續)

標題: Linux的TCP/IP協議棧閱讀筆記(2)

下面來看看*4協議和PACKET協議的初始化過程。

首先看PACKET協議,首先我們假定PACKET協議是編譯在核心里面的,
而不是一個MODULE,這樣得到packet_proto_init函數在net/packet/af_packet.c
里面是這樣的:
void __init packet_proto_init(struct net_proto *pro)
{
sock_register(&packet_family_ops);
register_netdevice_notifier(&packet_netdev_notifier);
}

其中sock_register函數在net/socket.c里面,就是簡單的設置前面
說的net_families數組中間對應的值:
int sock_register(struct net_proto_family *ops)
{
if (ops->family >= NPROTO) {
printk(KERN_CRIT "protocol %d >= NPROTO(%d)\n",
ops->family, NPROTO);
return -ENOBUFS;
}
net_families[ops->family]=ops;
return 0;
}

這里要說明的是packet_netdev_notifier是一個struct notifier_block
類型,這個struct是在include/linux/notifier.h里面的:
struct notifier_block
{
int (*notifier_call)(struct notifier_block *self, unsigned long, void *);
struct notifier_block *next;
int priority;
};

而register_netdevice_notifier函數在net/core/dev.c里面,是這樣的:
int register_netdevice_notifier(struct notifier_block *nb)
{
return notifier_chain_register(&netdev_chain, nb);
}

而notifier_chain_register函數在include/linux/notifier.h里面,是
這樣的:
extern __inline__ int notifier_chain_register(
struct notifier_block **list, struct notifier_block *n)
{
while(*list)
{
if(n->priority > (*list)->priority)
break;
list= &((*list)->next);
}
n->next = *list;
*list=n;
return 0;
}
顯然就是根據每個block的優先級把這個block排列在一個block的鏈表里
面,在notifier_chain_register函數里面我們可以發現這個鏈表是
netdev_chain。實際上這個鏈表的作用就是在每個interface打開,關閉
狀態改變或者外界調用相應的ioctl的時候通知這個鏈表上面的所有相關
的設備,而每一個協議都調用register_netdevice_notifier注冊了一個
netdev_notifier的結構體,這樣就可以在interface改變的時候得到通知
了(通過調用每個notifier_call函數)。

下面來看inet_proto_init函數,這個函數在net/*4/af_inet.c中間,
里面也有很多ifdef的編譯選項,假定下面幾個是沒有定義的:
CONFIG_NET_IPIP,CONFIG_NET_IPGRE,CONFIG_IP_FIREWALL,
CONFIG_IP_MASQUERADE,CONFIG_IP_MROUTE
假定下面幾個是定義了的:
CONFIG_INET_RARP,CONFIG_PROC_FS
下面是整理后的代碼:
(void) sock_register(&inet_family_ops);
for(p = inet_protocol_base; p != NULL;) {
struct inet_protocol *tmp=(struct inet_protocol *)p->next;
inet_add_protocol(p);
printk("%s%s",p->name,tmp?", ":"\n");
p = tmp;
}

arp_init();
ip_init();
tcp_v4_init(&inet_family_ops);
tcp_init();
icmp_init(&inet_family_ops);

rarp_ioctl_hook = rarp_ioctl;
proc_net_register(&proc_net_rarp);
proc_net_register(&proc_net_raw);
proc_net_register(&proc_net_snmp);
proc_net_register(&proc_net_netstat);
proc_net_register(&proc_net_sockstat);
proc_net_register(&proc_net_tcp);
proc_net_register(&proc_net_udp);

標題: Linux的TCP/IP協議棧閱讀筆記(3)

其中的sock_register函數的作用已經在前面說了,現在來看看
struct inet_protocol和inet_add_protocol函數。前面的結構體
是在include/net/protocol.h里面:
struct inet_protocol
{
int (*handler)(struct sk_buff *skb, unsigned short len);
void (*err_handler)(struct sk_buff *skb, unsigned *dp, int len);
struct inet_protocol *next;
unsigned protocol;
unsigned copy:1;
void *data;
const *name;
};
*個函數是用來接收數據的callback函數,第二個是錯誤處理函數,
其它的copy是用來協議共享的,這個以后再說,data當然就是這個結
構體的私有數據了。

inet_add_protocol函數是在net/*4/protocol.c里面的:
void inet_add_protocol(struct inet_protocol *prot)
{
unsigned hash;
struct inet_protocol *p2;

hash = prot->protocol & (MAX_INET_PROTOS - 1);
prot ->next = inet_protos[hash];
inet_protos[hash] = prot;
prot->copy = 0;

p2 = (struct inet_protocol *) prot->next;
while(p2 != NULL)
{
if (p2->protocol == prot->protocol)
{
prot->copy = 1;
break;
}
p2 = (struct inet_protocol *) p2->next;
}
}
顯然這個函數就是作樂一個hash表,然后每個hash表項都是一個鏈表頭,
然后通過這個hash表加鏈表的方式訪問每個協議結構體。在這里你也見
到了copy成員的用法了把。

arp_init函數是在net/*4/arp.c里面的(假定沒有定義CONFIG_SYSCTL):

neigh_table_init(&arp_tbl);
dev_add_pack(&arp_packet_type);
proc_net_register(&proc_net_arp);

不知道是不是有人眼睛一亮啊,呵呵,看到了dev_add_pack函數。
還是一步步來把。
neigh_table_init函數在net/core/neighbour.c中間:
void neigh_table_init(struct neigh_table *tbl)
{
unsigned long now = jiffies;

tbl->parms.reachable_time = neigh_rand_reach_time(
tbl->parms.base_reachable_time);

init_timer(&tbl->gc_timer);
tbl->gc_timer.data = (unsigned long)tbl;
tbl->gc_timer.function = neigh_periodic_timer;
tbl->gc_timer.expires = now + tbl->gc_interval +
tbl->parms.reachable_time;
add_timer(&tbl->gc_timer);

init_timer(&tbl->proxy_timer);
tbl->proxy_timer.data = (unsigned long)tbl;
tbl->proxy_timer.function = neigh_proxy_process;
skb_queue_head_init(&tbl->proxy_queue);

tbl->last_flush = now;
tbl->last_rand = now + tbl->parms.reachable_time*20;
tbl->next = neigh_tables;
neigh_tables = tbl;
}
jiffies是當前系統的時間,在i386系統上面好象一個jiffies代表50ms,
顯然這個函數就是生成兩個timer將一個放在系統的timerlist里面。
那個gc_timer的意思是garbage collect timer,因為每過一段時間arp
的cache就應該更新,所以要有一個expires時間,這段時間過了以后就
要更新arp地址了,那個proxy_timer還沒有看是什么,不過我假定我的
機器不使用proxy也不做成proxy,所以proxy相關的都沒有管:P
那個timer的function顯然是時鐘到期的回調函數,data是這個回調函數
要使用的私有數據了。

下面是dev_add_pack函數,它在net/core/dev.c里面:
void dev_add_pack(struct packet_type *pt)
{
int hash;
#Ifdef CONFIG_NET_FASTROUTE
/* Hack to detect packet socket */
if (pt->data) {
netdev_fastroute_obstacles++;
dev_clear_fastroute(pt->dev);
}
#Endif
if(pt->type==htons(ETH_P_ALL))
{
netdev_nit++;
pt->next=ptype_all;
ptype_all=pt;
}
else
{
hash=ntohs(pt->type)&15;
pt->next = ptype_base[hash];
ptype_base[hash] = pt;
}
}
顯然系統保留了兩個表,一個是ptype_all,用來接收所有類型的包的
鏈表,一個是一個hash數組+鏈表的結構,用來接收特定類型的包。那
個fastroute不知道是什么東西。

struct packet_type的定義在include/linux/netdevice.h里面,我保留
原來的注釋,這樣就不用我多說了:)
{
unsigned short type;
/* This is really htons(ether_type). */
struct device *dev;
/* NULL is wildcarded here */
int (*func) (struct sk_buff *,
struct device *, struct packet_type *);
void *data;
/* Private to the packet type */
struct packet_type *next;
};
其中的func當然是回調函數了,舉個例子來說,arp_packet_type是這樣的:
static struct packet_type arp_packet_type =
{
__constant_htons(ETH_P_ARP),
NULL, /* All devices */
arp_rcv,
NULL,
NULL
};

arp_init函數還有zui后一個proc_net_register函數,這個函數在include/
linux/proc_fs.h里面:
static inline int proc_net_register(struct proc_dir_entry * x)
{
return proc_register(proc_net, x);
}

而proc_register在fs/proc/root.c里面,這個函數就不帖了主要就是在
proc_net對應的目錄下面生成每個協議的子目錄。這個函數的作用就是對
每一個協議,例如TCP等在/proc目錄下面生成相應的目錄,用戶可以通過
訪問/proc/net目錄下面的相應目錄得到每個協議的統計參數。

標題: Linux的TCP/IP協議棧閱讀筆記(4)

下面是ip_init函數,它在net/*4/ip_output.c里面:(下面假定定
義了CONFIG_PROC_FS,CONFIG_IP_MULTICAST和CONFIG_NET_CLS_ROUTE)
__initfunc(void ip_init(void))
{
dev_add_pack(&ip_packet_type);
ip_rt_init();
proc_net_register(&proc_net_igmp);
}

前面的dev_add_pack是說過的,這里就不再說了,而且proc_net_register
也是前面提過的,這里都不說了,先來看看ip_rt_init函數把,它在net/
*4/route.c里面,函數是這樣的:
__initfunc(void ip_rt_init(void))
{
struct proc_dir_entry *ent;
devinet_init();
ip_fib_init();
rt_periodic_timer.function = rt_check_expire;
/* All the timers, started at system startup tend
to synchronize. Perturb it a bit.
*/
rt_periodic_timer.expires = jiffies + net_random()%
ip_rt_gc_interval + ip_rt_gc_interval;
add_timer(&rt_periodic_timer);

proc_net_register(&(struct proc_dir_entry) {
PROC_NET_RTCACHE, 8, "rt_cache",
S_IFREG | S_IRUGO, 1, 0, 0,
0, &proc_net_inode_operations,
rt_cache_get_info
});
ent = create_proc_entry("net/rt_acct", 0, 0);
ent->read_proc = ip_rt_acct_read;
}
這個函數總的看來就是注冊幾個notifier(后面還要看的)和初始化路由表的
timer,zui后就在/proc目錄下面創建一個目錄項。其中proc_net_register
函數就不說了,而create_proc_entry函數就是在/proc/net目錄下面創建一
個rt_acct,就是路由參數統計(account)目錄,讀函數就是ip_rt_acct_read,
這個函數就是從全局變量ip_rt_acct中間拷貝數據到用戶緩沖中而已。


devinet_init函數是net/*4/devinet.c里面的函數,整理后如下:
register_gifconf(PF_INET, inet_gifconf);
register_netdevice_notifier(&ip_netdev_notifier);

register_netdevice_notifier函數在說PACKET協議的時候提過,這里不說了,
register_gifconf函數是用來注冊對應SIOCGIFCONF這個系統調用的協議無關
的一個回調函數,這個函數對于PF_INET來說就是inet_gifconf函數。
其中inet_gifconf函數是net/*4/devinet.c里面的,我大概的看了一點,主
要好象是在所有的interface里面做一個循環,得到相應的name和address然后
返回的。不過不是非常確定。大家參謀呀:)

而register_gifconf函數本身是在net/core/dev.c里面的,如下:
static gifconf_func_t * gifconf_list [NPROTO];

int register_gifconf(unsigned int family, gifconf_func_t * gifconf)
{
if (family>=NPROTO)
return -EINVAL;
gifconf_list[family] = gifconf;
return 0;
}

這個函數的意義一目了然,就不說了。
gifconf_list里的函數會在dev_ifconf函數中間被調用,而dev_ifconf函數被
dev_ioctl函數調用,dev_ioctl函數負責所有的針對interface的I/O控制。所以
我們調用的interface的ioctl函數有一部分就會分到每個協議的gifconf函數里
面來,我猜gifconf大概是generous interface configure的意思。就是通用接
口配置的意思。

下面再看ip_fib_init函數,它在net/*4/fib_frontend.c中間,如下:
(假定沒有define CONFIG_IP_MULTIPLE_TABLES,這個參數好象是要創建
兩個路由表,一個是local的,一個叫main,我想大概一般系統上面只有
一個路由表的,所以認為沒有定義,不知道哪位清楚這個參數的意義?為
什么要兩個路由表?)
__initfunc(void ip_fib_init(void))
{
proc_net_register(&(struct proc_dir_entry) {
PROC_NET_ROUTE, 5, "route",
S_IFREG | S_IRUGO, 1, 0, 0,
0, &proc_net_inode_operations,
fib_get_procinfo
});

fib_rules_init();
register_netdevice_notifier(&fib_netdev_notifier);
register_inetaddr_notifier(&fib_inetaddr_notifier);
}

其中proc_net_register和register_netdevice_notifier函數上面已經提過
了,register_inetaddr_notifier函數的作用和register_netdevice_notifier
差不多,這個函數也是調用的notifier_chain_register函數注冊一個回調函
數,這個回調函數在interface加上和刪除的時候被調用,fib_rules_init函數
其實也差不多,這個函數在net/*4/fib_rules.c里面,它其實就是調用一個
register_netdevice_notifier函數注冊fib_rules_notifier回調結構體。
fib代表*4 Forwarding Information Base,就是*4轉發信息的意思,我有
點糊涂為什么要叫這個名字而不用route什么的更明白的//sigh

標題: (5)

下面是分析tcp_v4_init的時候了,這個函數在net/*4/tcp_*4.c里面:
__initfunc(void tcp_v4_init(struct net_proto_family *ops))
{
int err;

tcp_inode.i_mode = S_IFSOCK;
tcp_inode.i_sock = 1;
tcp_inode.i_uid = 0;
tcp_inode.i_gid = 0;

tcp_socket->inode = &tcp_inode;
tcp_socket->state = SS_UNCONNECTED;
tcp_socket->type=SOCK_RAW;

if ((err=ops->create(tcp_socket, IPPROTO_TCP))<0)
panic("Failed to create the TCP control socket.\n");
tcp_socket->sk->allocation=GFP_ATOMIC;
tcp_socket->sk->num = 256;
tcp_socket->sk->ip_ttl = MAXTTL;
}
tcp_inode當然就是一個inode節點了,而tcp_socket等于tcp_inode.u.socket_i,
通過一個指針他們指向同一個內存.
tcp_socket是用來通信使用的,可以叫TCP的control socket或者是communication
socket,當TCP通信沒有相應的socket的時候這個socket就充當了socket的角色.比
如在一個關閉端口上收到SYN時發送RST,或者是在三次握手的時候發送SYN(還沒有
accept產生新的socket)

值得注意的是ops->create函數的調用,我們前面見過對于AF_INET來說這個回調函
數是net/*4/af_inet.c的inet_create函數,這個函數是用來創建一個socket的
時候用的,由于函數比較長,這里先略過分析,這*次的分析只是一個大致流程的
熟悉而已.

由于有socket創建和通信,所以這段代碼是協議相關的,所以把這段代碼從原來的
tcp.c里面提取了出來

下面是tcp_init函數,它在net/*4/tcp.c里面,大體上來說就是創建了幾個hash表
和bucket.這段代碼創建了下面幾個全局對象:
tcp_openreq_cachep
tcp_bucket_cachep
tcp_timewait_cachep
tcp_ehash
tcp_bhash
其中ehash代表established hash, bhash代表bind hash,它們當然分別是所有的滿
足TCP_ESTABLISHED <= sk->state < TCP_CLOSE狀態的SOCK.但是我不清楚bucket
在這里是什么意思.anyone knows?那幾個cachep的作用也不是很清楚,特別是第二
個,根本摸不著頭腦.由于整個函數主要是內存分配和錯誤處理,這里不貼了.

再下來就是icmp_init函數了,在net/*4/icmp.c里面,事實上,如果把tcp_v4_init
里面的IPPROTO_TCP替換成IPPROTO_ICMP,基本都是一樣的.

剩下的proc_net_register函數前面已經講過了,這里就不說了.

到這里為止,Linux下面IP棧的開始的工作我們基本應該有了個了解,其中有幾個關鍵
的函數:

dev_add_pack:
注冊一個鏈路層以上的處理函數,一般是用來使用新的網絡層協議的,不過如果注冊
時重復也是可以的,這時候系統會設置一個copy位.如果是ETH_P_ALL則會接收所有
的數據包.加入的元素保存在ptype_all鏈表和ptype_base hash鏈表中間.

inet_add_protocol:
注冊一個建立在IP層以上的協議,例如TCP和UDP等

proc_net_register(還有類似的proc_register):
在/proc/net目錄下面創建一個子目錄項來使管理者能通過文件系統得到統計信息

現在迷惑的地方還有很多,一個是結構體sk_buff的每個成員的意義,一個是結構體
sock的意義,不過這兩個問題應該在以后看多了就知道了.
下面我就打算一個個分析每個協議的處理了,包括狀態轉化/數據發送/接收.

標題: (6)

let''s start from bottom up :)
有的時候用英語說話比漢語要簡潔和有意思一點:)

一個lance得到數據以后總會這樣處理:
skb = dev_alloc_skb (....);
skb->protocol = eth_type_trans(skb, dev);
....
netif_rx (skb);

eth_type_trans函數在net/ethernet/eth.c里面,作用當然很簡單了,大家可以
自己看;).
而netif_rx函數是在net/core/dev.c里面的,假定沒有定義CONFIG_CPU_IS_SLOW
(我覺得自己的CPU不慢:))和CONFIG_NET_HW_FLOWCONTROL(很少有人會意識到很
多網卡有流量控制把,不過沒有交換設備的支持,想憑這個東西達到Qos也沒什么
用)以后的代碼是這樣的:
void netif_rx(struct sk_buff *skb)
{
skb->stamp = xtime;

if (backlog.qlen <= netdev_max_backlog) {
if (backlog.qlen) {
if (netdev_dropping == 0) {
skb_queue_tail(&backlog,skb);
mark_bh(NET_BH);
return;
}
atomic_inc(&netdev_rx_dropped);
kfree_skb(skb);
return;
}
netdev_dropping = 0;
skb_queue_tail(&backlog,skb);
mark_bh(NET_BH);
return;
}
netdev_dropping = 1;
atomic_inc(&netdev_rx_dropped);
kfree_skb(skb);
}

xtime是當前的時間,一個struct timeval,利用gettimeofday函數得到的就是這個
東西的內容.backlog是一個sk_buff的雙向鏈表, netdev_dropping初始化為0,如果
沒有定義CONFIG_NET_HW_FLOWCONTROL,這個變量一直都將是0.skb_queue_tail就是
把一個sk_buff加入到backlog雙向隊列中去.然后mark_bh是設置了一個全局變量相
對位移NET_BH處的bit就返回了.這個bit的設置將使得內核下次schedule的時候從
TIMER_BH向下處理時檢查到NET_BH處發現有設置就會調用對應NET_BH優先級的函數
net_bh來處理,這個回調函數是在net_dev_init函數里面調用init_bh設置的,呵呵,
兄弟們,如果感興趣可以自己再init_bh看看設置一個自己的處理backlog的函數啊.
Linux在這里采取了一個古怪的策略進行控制權的轉移和處理機優先級的處理.另一
個函數net_bh來處理從backlog中間得到包,它是這樣的(假定沒定義CONFIG_BRIDGE
這個選項):
void net_bh(void)
{
struct packet_type *ptype;
struct packet_type *pt_prev;
unsigned short type;
unsigned long start_time = jiffies;

NET_PROFILE_ENTER(net_bh);

if (qdisc_head.forw != &qdisc_head)
qdisc_run_queues();


while (!skb_queue_empty(&backlog))
{
struct sk_buff * skb;

if (jiffies - start_time > 1)
goto net_bh_break;

skb = skb_dequeue(&backlog);

#Ifdef CONFIG_NET_FASTROUTE
if (skb->pkt_type == PACKET_FASTROUTE) {
dev_queue_xmit(skb);
continue;
}
#Endif

/* XXX until we figure out every place to modify.. */
skb->h.raw = skb->nh.raw = skb->data;

if(skb->mac.raw < skb->head || skb->mac.raw > skb->data){
printk(KERN_CRIT "%s: wrong mac.raw ptr, proto=%04x\n",
skb->dev->name, skb->protocol);
kfree_skb(skb);
continue;
}

type = skb->protocol;


pt_prev = NULL;
for (ptype = ptype_all; ptype!=NULL; ptype=ptype->next)
{
if (!ptype->dev || ptype->dev == skb->dev) {
if(pt_prev)
{
struct sk_buff *skb2=skb_clone(skb, GFP_ATOMIC);
if(skb2)
pt_prev->func(skb2,skb->dev, pt_prev);
}
pt_prev=ptype;
}
}

for (ptype = ptype_base[ntohs(type)&15]; ptype != NULL;
ptype = ptype->next)
{
if (ptype->type == type && (!ptype->dev ||
ptype->dev==skb->dev))
{
if(pt_prev)
{
struct sk_buff *skb2;
skb2=skb_clone(skb, GFP_ATOMIC);
if(skb2)
pt_prev->func(skb2, skb->dev, pt_prev);
}
pt_prev=ptype;
}
} /* End of protocol list loop */

if(pt_prev)
pt_prev->func(skb, skb->dev, pt_prev);
else {
kfree_skb(skb);
}
} /* End of queue loop */

if (qdisc_head.forw != &qdisc_head)
qdisc_run_queues();

netdev_dropping = 0;
NET_PROFILE_LEAVE(net_bh);
return;

net_bh_break:
mark_bh(NET_BH);
NET_PROFILE_LEAVE(net_bh);
return;
}

這個函數其實很簡單,NET_PROFILE_ENTER當然是一個宏展開了,它其實就是include/
net/profile.h里面的net_profile_enter函數,而NET_PROFILE_LEAVE是profile.h文
件里面的net_profile_leave函數,有興趣的看看把.:)幫我解疑.
qdisc_head是一個Qdisc_head類型,是一個全局變量,看名字和處理順序應該看作是一
個Quick DISCovery的隊列,如果不為空的話我們就要運行qdisc_run_queues函數進行
清理了,不過我并不清楚這個queue的意義,這個變量和函數都在net/sched/
sch_generic.c里面獲得的.大家看了給我答疑把,xixi

下面的東西挺簡單的,我就不說了,值得注意的是:
1.大家還記得ptype_all和ptype_base嗎?就是調用dev_add_pack加入的數組啊,zui終
也調用了pt_prev->func(....)
2.系統先處理ptype_all然后才處理的ptype_base
3.每處理一個sk_buff如果超過1jiffies(x86上為50ms)就再等待下次調用
4.sk_clone是一個快速拷貝,沒有拷貝數據,只是復制頭部而已

下次看packet的func函數處理把;)

標題: (7)

看看在net/packet/af_packet.c里面的packet_create函數,這個就是通過
packet_proto_init加入的回調函數,假設定義了CONFIG_SOCK_PACKET,代碼
整理如下,這個函數是在用戶創建鏈路層socket的時候被調用的:
static int packet_create(struct socket *sock, int protocol)
{
struct sock *sk;
int err;

if (!capable(CAP_NET_RAW))
return -EPERM;

if (sock->type != SOCK_DGRAM && sock->type != SOCK_RAW
&& sock->type != SOCK_PACKET

return -ESOCKTNOSUPPORT;
file://只有socket(AF_PACKET, [SOCK_DGRAM, SOCK_RAW],
file://或者socket(AF_INET, SOCK_PACKET ,才能調用成功

sock->state = SS_UNCONNECTED;
MOD_INC_USE_COUNT;

err = -ENOBUFS;

sk = sk_alloc(PF_PACKET, GFP_KERNEL, 1);
if (sk == NULL)
goto out;

sk->reuse = 1;
sock->ops = &packet_ops;
if (sock->type == SOCK_PACKET)
sock->ops = &packet_ops_spkt;
file://如果是old_style的SOCK_PACKET,就使用packet_ops_spkt
file://如果是AF_PACKET,就使用packet_ops作為對應的socket的
file://回調函數

sock_init_data(sock,sk);

sk->protinfo.af_packet = kmalloc(sizeof(struct packet_opt),
GFP_KERNEL);
file://protinfo是一個union

if (sk->protinfo.af_packet == NULL)
goto out_free;

memset(sk->protinfo.af_packet, 0, sizeof(struct packet_opt));

sk->zapped=0;
file://這個zapped屬性表示一個TCP的socket收到了RST

sk->family = PF_PACKET;
sk->num = protocol;

sk->protinfo.af_packet->prot_hook.func = packet_rcv;
if (sock->type == SOCK_PACKET)
sk->protinfo.af_packet->prot_hook.func = packet_rcv_spkt;
sk->protinfo.af_packet->prot_hook.data = (void *)sk;

if (protocol) {
sk->protinfo.af_packet->prot_hook.type = protocol;

dev_add_pack(&sk->protinfo.af_packet->prot_hook);
file://注意到了沒有,如果protocol非零的話也可以dev_add_pack
file://的,不過當然不能達到phrack55-12的目的,因為這時候你的
file://數據已經在用戶地址空間了,內核的數據也是改不了的

sk->protinfo.af_packet->running = 1;
}

sklist_insert_socket(&packet_sklist, sk);
file://這個函數顯然應該實現非常簡單,在net/core/sock.c里面.
file://packet_sklist是用來給每個socket通知interface狀態變化
file://的消息的,包括UP/DOWN/MULTICAST_LIST_CHANGE
file://這個回調函數的實現是我們說過的register_netdev_notifier

return(0);

out_free:
sk_free(sk);
out:
MOD_DEC_USE_COUNT;
return err;
}
只有在創建了packet socket以后應用程序才能接收鏈路層的數據包.而只有你設
置了一個非零的protocol以后才能dev_add_pack,你的socket才能接收數據的.現
在看來,dev_add_pack確實是實現底層數據改寫的一個重要的函數.所以下面我們
將注意dev_add_pack設置的回調函數func的使用.

標題: (8)

我們已經知道了,如果使用socket(AF_SOCKET, ..)產生一個PACKET SOCKET的話,
dev_add_pack加入的函數是packet_rcv,下面是這個在net/packet/af_packet.c里
面的函數:
static int packet_rcv(struct sk_buff *skb, struct device *dev,
struct packet_type *pt)
{
struct sock *sk;
struct sockaddr_ll *sll = (struct sockaddr_ll*)skb->cb;

sk = (struct sock *) pt->data;
file://我們在packet_create中令data = sk了,remember?

if (skb->pkt_type == PACKET_LOOPBACK) {
kfree_skb(skb);
return 0;
}

skb->dev = dev;

sll->sll_family = AF_PACKET;
sll->sll_hatype = dev->type;
sll->sll_protocol = skb->protocol;
sll->sll_pkttype = skb->pkt_type;
sll->sll_ifindex = dev->ifindex;
sll->sll_halen = 0;

if (dev->hard_header_parse)
sll->sll_halen = dev->hard_header_parse(skb, sll->sll_addr);

if (dev->hard_header)
if (sk->type != SOCK_DGRAM)
skb_push(skb, skb->data - skb->mac.raw);
else if (skb->pkt_type == PACKET_OUTGOING)
skb_pull(skb, skb->nh.raw - skb->data);

if (sock_queue_rcv_skb(sk,skb)<0)
{
kfree_skb(skb);
return 0;
}
return(0);
}
在這個函數里面有個地方我很疑惑,那就是pkt_type屬性,不知道這個屬性是什么
地方確定的,從網卡驅動->netif_rx->net_bh中間都沒有,而且甚至在net_bh函數
中間就有if(skb->pkt_type==...)這樣的語句,說明在驅動中間就設置了,但是沒
有找到,我faint了.這個屬性以后還會大量使用,還是很重要的.

這里還有幾個函數要說明:
skb_pull在include/linux/skbuff.h中間:
extern __inline__ *__skb_pull(struct sk_buff *skb,
unsigned int len)
{
skb->len-=len;
return skb->data+=len;
}

extern __inline__ unsigned * skb_pull(struct sk_buff *skb,
unsigned int len)
{
if (len > skb->len)
return NULL;
return __skb_pull(skb,len);
}
不過是把頭部的數據空出來,相應調整數據頭部data的地址和長度.

同樣skb_push在include/linux/skbuff.h中間:
extern __inline__ unsigned *__skb_push(struct sk_buff *skb,
unsigned int len)
{
skb->data-=len;
skb->len+=len;
return skb->data;
}

extern __inline__ unsigned *skb_push(struct sk_buff *skb,
unsigned int len)
{
skb->data-=len;
skb->len+=len;
if(skb->datahead)
{
__label__ here;
skb_under_panic(skb, len, &&here);
here: ;
}
return skb->data;
}
這個調整使數據長度加長,和skb_pull相反,不過skb_push顯然更加安全一點.

在上面的程序中間,如果設備有一個明確的link_level_header,就考慮要不要
調整數據長度和地址,如果sk->type不是SOCK_DGRAM的話,說明程序對整個數據
包包括ll地址都感興趣.這樣需要加長數據段使得數據包含ll頭部.不然如果數
據是向外走的,則需要把數據裁減到只包含從網絡層數據包頭開始的地方.所以
是從nh.raw剪掉data,這就是差值.(nh=network header)

經過了這些處理以后,現在的skb已經是可以提交的了,這樣就調用
sock_queue_rcv_skb函數將這個skb加入到相應socket的接收緩沖區中去. 這個
函數以后再說把.

標題: (8)
發信人: difeijing

: 在這個函數里面有個地方我很疑惑,那就是pkt_type屬性,不知道這個屬性是什么
: 地方確定的,從網卡驅動->netif_rx->net_bh中間都沒有,而且甚至在net_bh函數
: 中間就有if(skb->pkt_type==...)這樣的語句,說明在驅動中間就設置了,但是沒
: 有找到,我faint了.這個屬性以后還會大量使用,還是很重要的.
pkt_type的確是在驅動中設置的,所有的驅動中都有這樣一句:
skb->protocol=eth_type_trans(skb,dev);
eth_type_trans里對pkt_type進行了設置.
skb->mac.raw=skb->data;
skb_pull(skb,dev->hard_header_len);
eth= skb->mac.ethernet;
if(*eth->h_dest&1)
{
if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0)
skb->pkt_type=PACKET_BROADCAST;
else
skb->pkt_type=PACKET_MULTICAST;
}
else if(1 /*dev->flags&IFF_PROMISC*/)
{
if(memcmp(eth->h_dest,dev->dev_addr, ETH_ALEN))
skb->pkt_type=PACKET_OTHERHOST;
}
沒有找到對類型PACKET_HOST的顯式定義,大概因為初始化結構是是
填充的0,又有"#define PACKET_HOST 0",所以就不明確的去確定了.

這個eth_type_trans呀..
我看過得,不過好像pkt_type太少了一點,后來的
dev_add_pack里面的回調函數使用了很多種pkt_type的
判斷,不過我倒是沒有注意到PACKET_HOST是0,ft

 

Linux內核技術       
Thread views: 142
yawl
(stranger )
01/04/01 10:41 AM 補充:
那個Packet types是在eth.c中的eth_type_trans里面賦值的,而這個函數幾乎被所有的網卡驅動程序調用。其實也就這么幾種,都是針對鏈路層的:
#define PACKET_HOST                0                /* To us                */
#define PACKET_BROADCAST        1                /* To all                */
#define PACKET_MULTICAST        2                /* To group                */
#define PACKET_OTHERHOST        3                /* To someone else         */
#define PACKET_OUTGOING                4                /* Outgoing of any type */
/* These ones are invisible by user level */
#define PACKET_LOOPBACK                5                /* MC/BRD frame looped back */
#define PACKET_FASTROUTE        6                /* Fastrouted frame        */

全年征稿/資訊合作 聯系郵箱:1271141964@qq.com

免責聲明

  • 凡本網注明"來源:智能制造網"的所有作品,版權均屬于智能制造網,轉載請必須注明智能制造網,http://www.xashilian.com。違反者本網將追究相關法律責任。
  • 企業發布的公司新聞、技術文章、資料下載等內容,如涉及侵權、違規遭投訴的,一律由發布企業自行承擔責任,本網有權刪除內容并追溯責任。
  • 本網轉載并注明自其它來源的作品,目的在于傳遞更多信息,并不代表本網贊同其觀點或證實其內容的真實性,不承擔此類作品侵權行為的直接責任及連帶責任。其他媒體、網站或個人從本網轉載時,必須保留本網注明的作品來源,并自負版權等法律責任。
  • 如涉及作品內容、版權等問題,請在作品發表之日起一周內與本網聯系,否則視為放棄相關權利。

<
更多 >

工控網機器人儀器儀表物聯網3D打印工業軟件金屬加工機械包裝機械印刷機械農業機械食品加工設備制藥設備倉儲物流環保設備造紙機械工程機械紡織機械化工設備電子加工設備水泥設備海洋水利裝備礦冶設備新能源設備服裝機械印染機械制鞋機械玻璃機械陶瓷設備橡塑設備船舶設備電子元器件電氣設備


我要投稿
  • 投稿請發送郵件至:(郵件標題請備注“投稿”)1271141964.qq.com
  • 聯系電話0571-89719789
工業4.0時代智能制造領域“互聯網+”服務平臺
智能制造網APP

功能豐富 實時交流

智能制造網小程序

訂閱獲取更多服務

微信公眾號

關注我們

抖音

智能制造網

抖音號:gkzhan

打開抖音 搜索頁掃一掃

視頻號

智能制造網

公眾號:智能制造網

打開微信掃碼關注視頻號

快手

智能制造網

快手ID:gkzhan2006

打開快手 掃一掃關注
意見反饋
關閉
企業未開通此功能
詳詢客服 : 0571-87858618
主站蜘蛛池模板: 双城市| 兴城市| 阜平县| 潞城市| 镇赉县| 合作市| 大石桥市| 利川市| 德阳市| 景洪市| 高要市| 丹凤县| 任丘市| 珲春市| 沈丘县| 云霄县| 宣城市| 永德县| 东兰县| 万山特区| 秦安县| 高阳县| 双流县| 衡阳县| 龙井市| 渭源县| 西畴县| 东辽县| 历史| 青神县| 当涂县| 中江县| 高邮市| 巴楚县| 鄂温| 黄石市| 通州区| 邵阳市| 延安市| 梁山县| 囊谦县|