乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      TCP/IP協(xié)議棧初始化流程

       womking 2007-05-30
       

      TCP/IP協(xié)議棧初始化流程

         
      啟動以后

          先從init/main.c的start_kernel函數(shù)說起。
          在這個函數(shù)里面調用kernel_thread啟動了init進程,這個進程對應的函數(shù)是同一個文件里面的init函數(shù),在init函數(shù)里面調用了一個
      叫do_basic_setup的在同一個文件里面的函數(shù),這個函數(shù)調用
      了net/socket.c里面的sock_init函數(shù),這個函數(shù)就是TCP/IP協(xié)議棧,也包括ipx等的入口。

          首先sock_init函數(shù)里面有很多ifdef這樣的東東,我覺得對于一個普通的主機來說,這些都不會配置的,它們包括:
      SLAB_SKB,CONFIG_WAN_ROUTER,CONFIG_FIREWALL,CONFIG_RTNETLINK,CONFIG_NETLINK_DEV

          去掉了這些編譯選項以后就剩下這樣的代碼:

      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函數(shù)是每個協(xié)議,例如AF_INET等初始化上層協(xié)議如TCP/ICMP協(xié)議需要的,以后還會遇到的,這里先放著把

          sk_init函數(shù)在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函數(shù)在同一個文件里面:

      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 char *name; /* Protocol name */
      void (*init_func)(struct net_proto *); /* Bootstrap */
      };

          這個protocols的數(shù)組是在net/protocols.c里面定義的,包含了一堆的協(xié)議初始化結構體,其中我只注意兩個:AF_INET和AF_PACKET
      它們的初始化函數(shù)分別是inet_proto_init和packet_proto_init




      協(xié)議初始化-1

      下面來看看IPv4協(xié)議和PACKET協(xié)議的初始化過程。

          首先看PACKET協(xié)議,首先我們假定PACKET協(xié)議是編譯在核心里面的,而不是一個MODULE,這樣得到packet_proto_init函數(shù)在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函數(shù)在net/socket.c里面,就是簡單的設置前面說的net_families數(shù)組中間對應的值:

      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函數(shù)在net/core/dev.c里面,是這樣的:

      int register_netdevice_notifier(struct notifier_block *nb)
      {
      return notifier_chain_register(&netdev_chain, nb);
      }

          而notifier_chain_register函數(shù)在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;
      }

          顯然就是根據(jù)每個block的優(yōu)先級把這個block排列在一個block的鏈表里面,在notifier_chain_register函數(shù)里面我們可以發(fā)現(xiàn)這個鏈表是netdev_chain。實際上這個鏈表的作用就是在每個interface打開,關閉狀態(tài)改變或者外界調用相應的ioctl的時候通知這個鏈表上面的所有相關的設備,而每一個協(xié)議都調用register_netdevice_notifier注冊了一個netdev_notifier的結構體,這樣就可以在interface改變的時候得到通知了(通過調用每個notifier_call函數(shù))。

          下面來看inet_proto_init函數(shù),這個函數(shù)在net/ipv4/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);



      協(xié)議初始化-2

          其中的sock_register函數(shù)的作用已經(jīng)在前面說了,現(xiàn)在來看看struct inet_protocol和inet_add_protocol函數(shù)。前面的結構體
      是在include/net/protocol.h里面:

      struct inet_protocol
      {
      int (*handler)(struct sk_buff *skb, unsigned short len);
      void (*err_handler)(struct sk_buff *skb, unsigned char *dp, int len);
      struct inet_protocol *next;
      unsigned char protocol;
      unsigned char copy:1;
      void *data;
      const char *name;
      };

          第一個函數(shù)是用來接收數(shù)據(jù)的callback函數(shù),第二個是錯誤處理函數(shù),其它的copy是用來協(xié)議共享的,這個以后再說,data當然就是這個結構體的私有數(shù)據(jù)了。

          inet_add_protocol函數(shù)是在net/ipv4/protocol.c里面的:

      void inet_add_protocol(struct inet_protocol *prot)
      {
      unsigned char 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;
      }
      }
          顯然這個函數(shù)就是建立一個hash表,然后每個hash表項都是一個鏈表頭,然后通過這個hash表加鏈表的方式訪問每個協(xié)議結構體。在這里你也見到了copy成員的用法了把。

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

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

          不知道是不是有人眼睛一亮啊,呵呵,看到了dev_add_pack函數(shù)。
          還是一步步來把。

          neigh_table_init函數(shù)在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是當前系統(tǒng)的時間,在i386系統(tǒng)上面好象一個jiffies代表50ms,顯然這個函數(shù)就是生成兩個timer將一個放在系統(tǒng)的timerlist里面。那個gc_timer的意思是garbage collect timer,因為每過一段時間arp的cache就應該更新,所以要有一個expires時間,這段時間過了以后就要更新arp地址了,那個proxy_timer還沒有看是什么,不過我假定我的機器不使用proxy也不做成proxy,所以proxy相關的都沒有管:P
          那個timer的function顯然是時鐘到期的回調函數(shù),data是這個回調函數(shù)要使用的私有數(shù)據(jù)了。

          下面是dev_add_pack函數(shù),它在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;
      }
      }
          顯然系統(tǒng)保留了兩個表,一個是ptype_all,用來接收所有類型的包的鏈表,一個是一個hash數(shù)組+鏈表的結構,用來接收特定類型的包。

          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當然是回調函數(shù)了,舉個例子來說,arp_packet_type是這樣的:

      static struct packet_type arp_packet_type =
      {
      __constant_htons(ETH_P_ARP),
      NULL, /* All devices */
      arp_rcv,
      NULL,
      NULL
      };

          arp_init函數(shù)還有最后一個proc_net_register函數(shù),這個函數(shù)在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對應的目錄下面生成每個協(xié)議的子目錄,例如TCP等在/proc目錄下面生成相應的目錄,用戶可以通過訪問/proc/net目錄下面的相應目錄得到每個協(xié)議的統(tǒng)計參數(shù)。




      協(xié)議初始化-3

          下面是ip_init函數(shù),它在net/ipv4/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函數(shù)把,它在net/ipv4/route.c里面,函數(shù)是這樣的:

      __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;
      }

          這個函數(shù)總的看來就是注冊幾個notifier(后面還要看的)和初始化路由表的timer,最后就在/proc目錄下面創(chuàng)建一個目錄項。其中proc_net_register函數(shù)就不說了,而create_proc_entry函數(shù)就是在/proc/net目錄下面創(chuàng)建一個rt_acct,就是路由參數(shù)統(tǒng)計(account)目錄,讀函數(shù)就是ip_rt_acct_read,這個函數(shù)就是從全局變量ip_rt_acct中間拷貝數(shù)據(jù)到用戶緩沖中而已。

          devinet_init函數(shù)是net/ipv4/devinet.c里面的函數(shù),整理后如下:

      register_gifconf(PF_INET, inet_gifconf);
      register_netdevice_notifier(&ip_netdev_notifier);

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

          而register_gifconf函數(shù)本身是在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;
      }

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

          下面再看ip_fib_init函數(shù),它在net/ipv4/fib_frontend.c中間,如下:
          (假定沒有define CONFIG_IP_MULTIPLE_TABLES,這個參數(shù)好象是要創(chuàng)建兩個路由表,一個是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函數(shù)上面已經(jīng)提過了,register_inetaddr_notifier函數(shù)的作用和register_netdevice_notifier差不多,這個函數(shù)也是調用的notifier_chain_register函數(shù)注冊一個回調函數(shù),這個回調函數(shù)在interface加上和刪除的時候被調用,fib_rules_init函數(shù)其實也差不多,這個函數(shù)在net/ipv4/fib_rules.c里面,它其實就是調用一個
      register_netdevice_notifier函數(shù)注冊fib_rules_notifier回調結構體。
          fib代表IPv4 Forwarding Information Base,就是IPv4轉發(fā)信息的意思




      協(xié)議初始化-4

          下面是分析tcp_v4_init的時候了,這個函數(shù)在net/ipv4/tcp_ipv4.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節(jié)點了,而tcp_socket等于tcp_inode.u.socket_i,通過一個指針他們指向同一個內存.
          tcp_socket是用來通信使用的,可以叫TCP的control socket或者是communicationsocket,當TCP通信沒有相應的socket的時候這個socket就充當了socket的角色.比如在一個關閉端口上收到SYN時發(fā)送RST,或者是在三次握手的時候發(fā)送SYN(還沒有accept產生新的socket)

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

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

          下面是tcp_init函數(shù),它在net/ipv4/tcp.c里面,大體上來說就是創(chuàng)建了幾個hash表和bucket.這段代碼創(chuàng)建了下面幾個全局對象:

      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狀態(tài)的SOCK.但是我不清楚bucket在這里是什么意思.anyone knows?那幾個cachep的作用也不是很清楚.由于整個函數(shù)主要是內存分配和錯誤處理,這里不貼了.

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

          剩下的proc_net_register函數(shù)前面已經(jīng)講過了,這里就不說了.

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

      dev_add_pack:

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

      inet_add_protocol:

          注冊一個建立在IP層以上的協(xié)議,例如TCP和UDP等

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

          現(xiàn)在迷惑的地方還有很多,一個是結構體sk_buff的每個成員的意義,一個是結構體sock的意義,不過這兩個問題應該在以后看多了就知道了.下面我就打算一個個分析每個協(xié)議的處理了,包括狀態(tài)轉化/數(shù)據(jù)發(fā)送/接收.
       
      let's start from bottom up :)有的時候用英語說話比漢語要簡潔和有意思一點:)

          一個lance得到數(shù)據(jù)以后總會這樣處理:

      skb = dev_alloc_skb (....);
      skb->protocol = eth_type_trans(skb, dev);
      ....
      netif_rx (skb);

          eth_type_trans函數(shù)在net/ethernet/eth.c里面,作用當然很簡單了,大家可以自己看;).
          而netif_rx函數(shù)是在net/core/dev.c里面的,假定沒有定義CONFIG_CPU_IS_SLOW(我覺得自己的CPU不慢:))和CONFIG_NET_HW_FLOWCONTROL(很少有人會意識到很多網(wǎng)卡有流量控制把,不過沒有交換設備的支持,想憑這個東西達到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函數(shù)得到的就是這個東西的內容.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處發(fā)現(xiàn)有設置就會調用對應NET_BH優(yōu)先級的函數(shù)net_bh來處理,這個回調函數(shù)是在net_dev_init函數(shù)里面調用init_bh設置的,呵呵,兄弟們,如果感興趣可以自己再init_bh看看設置一個自己的處理backlog的函數(shù)啊.

          Linux在這里采取了一個古怪的策略進行控制權的轉移和處理機優(yōu)先級的處理.另一個函數(shù)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;
      }

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

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




      packet 函數(shù)

          看看在net/packet/af_packet.c里面的packet_create函數(shù),這個就是通過packet_proto_init加入的回調函數(shù),假設定義了CONFIG_SOCK_PACKET,代碼整理如下,這個函數(shù)是在用戶創(chuàng)建鏈路層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;
      //只有socket(AF_PACKET, [SOCK_DGRAM, SOCK_RAW],
      //或者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;
      //如果是old_style的SOCK_PACKET,就使用packet_ops_spkt
      //如果是AF_PACKET,就使用packet_ops作為對應的socket的
      //回調函數(shù)

      sock_init_data(sock,sk);

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

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

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

      sk->zapped=0;
      //這個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);
      //注意到了沒有,如果protocol非零的話也可以dev_add_pack
      //的,不過當然不能達到phrack55-12的目的,因為這時候你的
      //數(shù)據(jù)已經(jīng)在用戶地址空間了,內核的數(shù)據(jù)也是改不了的

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

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

      return(0);

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




      packet_rcv

          我們已經(jīng)知道了,如果使用socket(AF_SOCKET, ..)產生一個PACKET SOCKET的話,dev_add_pack加入的函數(shù)是packet_rcv,下面是這個在net/packet/af_packet.c里面的函數(shù):

      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;
      //我們在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屬性是什么地方確定的?

          這里還有幾個函數(shù)要說明:

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

      extern __inline__ unsigned char * skb_pull(struct sk_buff *skb,
      unsigned int len)
      {
      if (len > skb->len)
      return NULL;
      return __skb_pull(skb,len);
      }

          不過是把頭部的數(shù)據(jù)空出來,相應調整數(shù)據(jù)頭部data的地址和長度.

          同樣skb_push在include/linux/skbuff.h中間:

      extern __inline__ unsigned char *__skb_push(struct sk_buff *skb,
      unsigned int len)
      {
      skb->data-=len;
      skb->len+=len;
      return skb->data;
      }

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

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

          經(jīng)過了這些處理以后,現(xiàn)在的skb已經(jīng)是可以提交的了,這樣就調用sock_queue_rcv_skb函數(shù)將這個skb加入到相應socket的接收緩沖區(qū)中去.

        本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
        轉藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多