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

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

    • 分享

      Linux內核實踐 - 如何添加網絡協(xié)議[二]:實現

       杰的個人圖書館 2012-04-11

      內核版本:2.6.34

      實現思路:
            報文在網絡協(xié)議棧中的流動,對于接收來講,是對報文的脫殼的過程,由于報文是已知的輸入,只要逐個解析協(xié)議號;對于發(fā)送來講,是各層發(fā)送函數 的嵌套調用,由于沒有已知的輸入,只能按事先設計好的協(xié)議進行層層構造。但無論報文怎樣的流動,核心是報文所在設備(skb->dev)的變化,相 當于各層之間傳遞的交接棒。
            按照上述思路,brcm協(xié)議接收的處理作為模塊brcm_packet_type加入到ptype_base中就可以了;brcm協(xié)議發(fā)送的處理則復雜一 點,發(fā)送的嵌套調用完全是依賴于設備來推動的,因此要有一種新創(chuàng)建的設備X,插入到vlan設備和網卡設備之間。
      因此,至少要有brcm_packet_type來加入ptype_base和register_brcm_dev()來向系統(tǒng)注冊設備X。進一步考慮, 設備X在全局量init_net中有存儲,但我們還需要知道設備X與vlan設備以及網卡設備是何種組織關系,所以在這里設計了 brcm_group_hash來存儲這種關系。為了對設備感興趣的事件作出響應,添加自己的notifier到netdev_chain中。另外,為了 用戶空間具有一定控制能力(如創(chuàng)建、刪除),還需要添加brcm相關的ioctl調用。為了讓它看起來更完整,一種新的設備在proc中也應有對應項,用 來調試和查看設備。
       

      從最簡單開始
            要讓網絡協(xié)議棧能夠接收一種新協(xié)議是很簡單的,由于已經有報文作為輸入,我們要做的僅僅是編寫好brcm_packet_type,然后在注冊模塊時只用做一件事:dev_add_pack。

      1. static int __init brcm_proto_init(void)  
      2. {  
      3.  dev_add_pack(&brcm_packet_type);  
      4. }  
      5.   
      6. static struct packet_type brcm_packet_type __read_mostly = {  
      7.  .type = cpu_to_be16(ETH_P_BRCM),  
      8.  .func = brcm_skb_recv, /* BRCM receive method */  
      9. };  
      10.   
      11. int brcm_skb_recv(struct sk_buff *skb, struct net_device *dev,  
      12.     struct packet_type *ptype, struct net_device *orig_dev)  
      13. {  
      14.  struct brcm_hdr *bhdr;  
      15.  struct brcm_rx_stats *rx_stats;  
      16.   
      17.  skb = skb_share_check(skb, GFP_ATOMIC);  
      18.  if(!skb)  
      19.   goto err_free;  
      20.  bhdr = (struct brcm_hdr *)skb->data;  
      21.   
      22.  rcu_read_lock();  
      23.  skb_pull_rcsum(skb, BRCM_HLEN);  
      24.  // set protocol  
      25.  skb->protocol = bhdr->brcm_encapsulated_proto;  
      26.  // reorder skb  
      27.  skb = brcm_check_reorder_header(skb);  
      28.  if (!skb)   
      29.   goto err_unlock;  
      30.    
      31.  netif_rx(skb);  
      32.  rcu_read_unlock();  
      33.  return NET_RX_SUCCESS;  
      34.   
      35. err_unlock:  
      36.  rcu_read_unlock();  
      37.   
      38. err_free:  
      39.  kfree_skb(skb);  
      40.  return NET_RX_DROP;  
      41. }  

            注冊這個模塊后,協(xié)議棧就能正常接收帶brcm報頭的報文的,代碼中ETH_P_BRCM是brcm的協(xié)議號,BRCM_HLEN是brcm的報頭長度。正是由于有報文作為輸入,接收變得十分簡單。
            但這僅僅是能接收而已,發(fā)送的報文還是不帶brcm報頭的,而且接收的這段代碼也很粗略,沒有變更skb的設備,沒有記錄流量,沒有對brcm報頭作有意義的處理,下面逐一進行添加。

      設備的相關定義
            一種設備就是net_device類型,而每種設備都有自己的私有變量,它存儲在net_device末尾,定義如下,其中real_dev指向下層設備,這是最基本屬性,其余可以視需要自己設定,brcm_rx_stats則是該設備接收流量統(tǒng)計:

      1. struct brcm_dev_info{  
      2.  struct net_device  *real_dev;  
      3.  u16 brcm_port;  
      4.  unsigned char  real_dev_addr[ETH_ALEN];  
      5.  struct proc_dir_entry *dent;  
      6.  struct brcm_rx_stats __percpu  *brcm_rx_stats;  
      7. };  
      8. struct brcm_rx_stats {  
      9.  unsigned long rx_packets;  
      10.  unsigned long rx_bytes;  
      11.  unsigned long multicast;  
      12.  unsigned long rx_errors;  
      13. };  

       設備間的關系問題
            如果brcm僅僅是只有一個設備,則無需數據結構來存儲這種關系,一個全局全變的brcm_dev就可以了。這里的設計考慮的是復雜的情況,可以存在多個 下層設備,多個brcm設備,之間沒有固定的關系。所以需要一種數據結構來存儲這種關系- brcm_group_hash。下面是一個簡單的圖示: 

            各個數據結構定義如下:

      1. static struct hlist_head brcm_group_hash[BRCM_GRP_HASH_SIZE];  
      2. struct brcm_group {  
      3.  struct hlist_node hlist;  
      4.  struct net_device *real_dev;  
      5.  int nr_ports;  
      6.  int killall;  
      7.  struct net_device *brcm_devices_array[BRCM_GROUP_ARRAY_LEN];  
      8.  struct rcu_head  rcu;  
      9. };  

            brcm_group_hash作為全局變量存在,以hash表形式組織,brcm_group被插入到brcm_group_hash 中,brcm_group存儲了它與下層設備的關系(eth與brcm),real_dev指向e下層設備,而brcm設備則存儲在 brcm_devices_array數組中。
           下面完成由下層設備轉換成brcm設備的函數,brcm_port是報頭中的值,可以自己設定它的含義,這里設定它表示報文來自于哪個端口。

      1. struct net_device *find_brcm_dev(struct net_device *real_dev, u16 brcm_port)  
      2. {  
      3.  struct brcm_group *grp = brcm_find_group(real_dev);  
      4.  if (grp)   
      5.   brcm_dev = grp->brcm_devices_array[brcm_port];  
      6.  return NULL;  
      7. }  

           因為在接收報文時,報文到達brcm層開始處理時,skb->dev指向的仍是下層設備,這時通過skb->dev查到 brcm_group->real_dev相匹配的hash項,然后通過報文brcm報頭的信息,確定 brcm_group->brcm_devices_array中哪個brcm設備作為skb的新設備;
           而在發(fā)送報文時,報文到達brcm層開始處理時,skb->dev指向的是brcm設備,為了繼續(xù)向下傳遞,需要變更為它的下層設備,在設備數據 net_device的私有數據部分,一般會存儲一個指針,指向它的下層設備,因此skb->dev只要變更為 brcm_dev_info(dev)->real_dev。
       

      流量統(tǒng)計
            在數據結構中,brcm設備的私有數據brcm_dev_info中brcm_rx_stats記錄接收的流量信息;而dev->_tx[index]則會記錄發(fā)送的流量信息。
            在接收函數brcm_skb_rcv()中對于成功接收的報文會增加流量統(tǒng)計:

      1. rx_stats = per_cpu_ptr(brcm_dev_info(skb->dev)->brcm_rx_stats,  
      2.   smp_processor_id());  
      3. rx_stats->rx_packets++;  
      4. rx_stats->rx_bytes += skb->len;  

            在發(fā)送函數brcm_dev_hard_start_xmit()中對于發(fā)送的報文會增加相應流量統(tǒng)計:

      1. if (likely(ret == NET_XMIT_SUCCESS)) {  
      2. txq->tx_packets++;  
      3. txq->tx_bytes += len;  
      4. else  
      5.  txq->tx_dropped++;  

            而brcm_netdev_ops->ndo_get_stats()即brcm_dev_get_stats()函數,則會將brcm網卡設備中 記錄的發(fā)送和接收流量信息匯總成通用的格式net_device_stats,像ifconfig等命令使用的就是net_device_stats轉換 后的結果。 

      完整收發(fā)函數
            有了這些后接收函數brcm_skb_recv()就可以完整了,其中關于報頭brcm_hdr的處理可以略過,由于是空想的協(xié)議,含義是可以自己設定的:

      1. int brcm_skb_recv(struct sk_buff *skb, struct net_device *dev,  
      2.     struct packet_type *ptype, struct net_device *orig_dev)  
      3. {  
      4.  struct brcm_hdr *bhdr;  
      5.  struct brcm_rx_stats *rx_stats;  
      6.  int op, brcm_port;  
      7.   
      8.  skb = skb_share_check(skb, GFP_ATOMIC);  
      9.  if(!skb)  
      10.   goto err_free;  
      11.  bhdr = (struct brcm_hdr *)skb->data;  
      12.  op = bhdr->brcm_tag.brcm_53242_op;  
      13.  brcm_port = bhdr->brcm_tag.brcm_53242_src_portid- 23;  
      14.   
      15.  rcu_read_lock();  
      16.   
      17.  // drop wrong brcm tag packet  
      18.  if (op != BRCM_RCV_OP || brcm_port < 1   
      19.   || brcm_port > 27)   
      20.   goto err_unlock;  
      21.   
      22.  skb->dev = find_brcm_dev(dev, brcm_port);  
      23.  if (!skb->dev) {  
      24.   goto err_unlock;  
      25.  }  
      26.   
      27.  rx_stats = per_cpu_ptr(brcm_dev_info(skb->dev)->brcm_rx_stats,  
      28.           smp_processor_id());  
      29.  rx_stats->rx_packets++;  
      30.  rx_stats->rx_bytes += skb->len;  
      31.  skb_pull_rcsum(skb, BRCM_HLEN);  
      32.   
      33.  switch (skb->pkt_type) {  
      34.  case PACKET_BROADCAST: /* Yeah, stats collect these together.. */  
      35.   /* stats->broadcast ++; // no such counter :-( */  
      36.   break;  
      37.   
      38.  case PACKET_MULTICAST:  
      39.   rx_stats->multicast++;  
      40.   break;  
      41.   
      42.  case PACKET_OTHERHOST:  
      43.   /* Our lower layer thinks this is not local, let's make sure. 
      44.    * This allows the VLAN to have a different MAC than the 
      45.    * underlying device, and still route correctly. 
      46.    */  
      47.   if (!compare_ether_addr(eth_hdr(skb)->h_dest,  
      48.      skb->dev->dev_addr))  
      49.    skb->pkt_type = PACKET_HOST;  
      50.   break;  
      51.  default:  
      52.   break;  
      53.  }  
      54.    
      55.  // set protocol  
      56.  skb->protocol = bhdr->brcm_encapsulated_proto;  
      57.   
      58.  // reorder skb  
      59.  skb = brcm_check_reorder_header(skb);  
      60.  if (!skb) {  
      61.   rx_stats->rx_errors++;  
      62.   goto err_unlock;  
      63.  }  
      64.    
      65.  netif_rx(skb);  
      66.  rcu_read_unlock();  
      67.  return NET_RX_SUCCESS;  
      68.   
      69. err_unlock:  
      70.  rcu_read_unlock();  
      71.   
      72. err_free:  
      73.  kfree_skb(skb);  
      74.  return NET_RX_DROP;  
      75. }  

            同時,發(fā)送函數brcm_dev_hard_start_xmit()可以完整了,同樣,其中關于brcm_hdr的處理可以略過:

      1. static netdev_tx_t brcm_dev_hard_start_xmit(struct sk_buff *skb,  
      2.          struct net_device *dev)  
      3. {  
      4.  int i = skb_get_queue_mapping(skb);  
      5.  struct netdev_queue *txq = netdev_get_tx_queue(dev, i);  
      6.  struct brcm_ethhdr *beth = (struct brcm_ethhdr *)(skb->data);  
      7.  unsigned int len;  
      8.  u16 brcm_port;  
      9.  int ret;  
      10.   
      11.  /* Handle non-VLAN frames if they are sent to us, for example by DHCP. 
      12.   * 
      13.   * NOTE: THIS ASSUMES DIX ETHERNET, SPECIFICALLY NOT SUPPORTING 
      14.   * OTHER THINGS LIKE FDDI/TokenRing/802.3 SNAPs... 
      15.   */  
      16.  if (beth->h_brcm_proto != htons(ETH_P_BRCM)){  
      17.   //unsigned int orig_headroom = skb_headroom(skb);  
      18.   brcm_t brcm_tag;  
      19.   brcm_port = brcm_dev_info(dev)->brcm_port;  
      20.   if (brcm_port == BRCM_ANY_PORT) {  
      21.    brcm_tag.brcm_op_53242 = 0;  
      22.    brcm_tag.brcm_tq_53242 = 0;  
      23.    brcm_tag.brcm_te_53242 = 0;  
      24.    brcm_tag.brcm_dst_53242 = 0;  
      25.   }else {  
      26.    brcm_tag.brcm_op_53242 = BRCM_SND_OP;  
      27.    brcm_tag.brcm_tq_53242 = 0;  
      28.    brcm_tag.brcm_te_53242 = 0;  
      29.    brcm_tag.brcm_dst_53242 = brcm_port + 23;  
      30.   }  
      31.   
      32.   skb = brcm_put_tag(skb, *(u32 *)(&brcm_tag));  
      33.   if (!skb) {  
      34.    txq->tx_dropped++;  
      35.    return NETDEV_TX_OK;  
      36.   }  
      37.  }  
      38.   
      39.  skb_set_dev(skb, brcm_dev_info(dev)->real_dev);  
      40.  len = skb->len;  
      41.  ret = dev_queue_xmit(skb);  
      42.   
      43.  if (likely(ret == NET_XMIT_SUCCESS)) {  
      44.   txq->tx_packets++;  
      45.   txq->tx_bytes += len;  
      46.  } else  
      47.   txq->tx_dropped++;  
      48.   
      49.  return ret;  
      50. }  

      注冊設備
            接收通過dev_add_pack(),就可以融入協(xié)議棧了,前面幾篇的分析已經講過通過ptype_base對報文進行脫殼?,F在要融入的發(fā)送,函數已 經完成了,既然發(fā)送是一種嵌套的調用,并且是由dev來推過的,那么發(fā)送函數的融入一定在設備進行注冊時,作為設備的一種發(fā)送方法。
            創(chuàng)建一種設備時,一定會有設備的XXX_setup()初始化,大部分設備都會用ether_setup()來作初始化,再進行適當更改。下面是brcm_setup():

      1. void brcm_setup(struct net_device *dev)  
      2. {  
      3.  ether_setup(dev);  
      4.   
      5.  dev->priv_flags  |= IFF_BRCM_TAG;  
      6.  dev->priv_flags  &= ~IFF_XMIT_DST_RELEASE;  
      7.  dev->tx_queue_len = 0;  
      8.   
      9.  dev->netdev_ops  = &brcm_netdev_ops;  
      10.  dev->destructor  = free_netdev;  
      11.  dev->ethtool_ops = &brcm_ethtool_ops;  
      12.   
      13.  memset(dev->broadcast, 0, ETH_ALEN);  
      14. }  

            其中發(fā)送函數就在brcm_netdev_ops中,每層設備都會這樣調用:dev->netdev_ops->ndo_start_xmit()。     

      1. static const struct net_device_ops brcm_netdev_ops = {  
      2.     .ndo_change_mtu     = brcm_dev_change_mtu,  
      3.     .ndo_init       = brcm_dev_init,  
      4.     .ndo_uninit     = brcm_dev_uninit,  
      5.     .ndo_open       = brcm_dev_open,  
      6.     .ndo_stop       = brcm_dev_stop,  
      7.     .ndo_start_xmit =  brcm_dev_hard_start_xmit,  
      8.     .ndo_validate_addr  = eth_validate_addr,  
      9.     .ndo_set_mac_address    = brcm_dev_set_mac_address,  
      10.     .ndo_set_rx_mode    = brcm_dev_set_rx_mode,  
      11.     .ndo_set_multicast_list = brcm_dev_set_rx_mode,  
      12.     .ndo_change_rx_flags    = brcm_dev_change_rx_flags,  
      13.     //.ndo_do_ioctl     = brcm_dev_ioctl,  
      14.     .ndo_neigh_setup    = brcm_dev_neigh_setup,  
      15.     .ndo_get_stats      = brcm_dev_get_stats,  
      16. };  
             而設備的初始化應該發(fā)生在創(chuàng)建設備時,也就是向網絡注冊它時,也就是register_brcm_dev(),注冊一個新設備,需要知道它的下層設備 real_dev以及唯一標識brcm設備的brcm_port。首先確定該設備沒有被創(chuàng)建,然后用alloc_netdev_mq創(chuàng)建新設備 new_dev,然后設置相關屬性,特別是它的私有屬性brcm_dev_info(new_dev),然后添加它到brcm_group_hash中, 最后發(fā)生真正的注冊register_netdevice()。
      1. static int register_brcm_dev(struct net_device *real_dev, u16 brcm_port)  
      2. {  
      3.  struct net_device *new_dev;  
      4.  struct net *net = dev_net(real_dev);  
      5.  struct brcm_group *grp;  
      6.  char name[IFNAMSIZ];  
      7.  int err;  
      8.   
      9.  if(brcm_port >= BRCM_PORT_MASK)  
      10.   return -ERANGE;  
      11.   
      12.  // exist yet  
      13.  if (find_brcm_dev(real_dev, brcm_port) != NULL)  
      14.   return -EEXIST;  
      15.   
      16.  snprintf(name, IFNAMSIZ, "brcm%i", brcm_port);  
      17.  new_dev = alloc_netdev_mq(sizeof(struct brcm_dev_info), name,  
      18.       brcm_setup, 1);  
      19.  if (new_dev == NULL)  
      20.   return -ENOBUFS;  
      21.  new_dev->real_num_tx_queues = real_dev->real_num_tx_queues;  
      22.  dev_net_set(new_dev, net);  
      23.  new_dev->mtu = real_dev->mtu;  
      24.   
      25.  brcm_dev_info(new_dev)->brcm_port = brcm_port;  
      26.  brcm_dev_info(new_dev)->real_dev = real_dev;  
      27.  brcm_dev_info(new_dev)->dent = NULL;  
      28.  //new_dev->rtnl_link_ops = &brcm_link_ops;  
      29.   
      30.  grp = brcm_find_group(real_dev);  
      31.  if (!grp)  
      32.   grp = brcm_group_alloc(real_dev);  
      33.    
      34.  err = register_netdevice(new_dev);  
      35.  if (err < 0)  
      36.   goto out_free_newdev;  
      37.    
      38.  /* Account for reference in struct vlan_dev_info */  
      39.  dev_hold(real_dev);  
      40.  brcm_group_set_device(grp, brcm_port, new_dev);  
      41.   
      42.  return 0;  
      43.   
      44. out_free_newdev:  
      45.  free_netdev(new_dev);  
      46.  return err;  
      47. }  

      ioctl
            由于brcm設備可以存在多個,并且和下層設備不是固定的對應關系,因此它的創(chuàng)建應該可以人為控制,因此通過ioctl由用戶進行創(chuàng)建。這里只為brcm 提供了兩種操作-添加與刪除。一種設備添加一定是與下層設備成關系的,因此添加時需要手動指明這種下層設備,然后通過 __dev_get_by_name()從網絡空間中找到這種設備,就可以調用register_brcm_dev()來完成注冊了。而設備的刪除則是直 接刪除,直接刪除unregister_brcm_dev()。
           

      1. static int brcm_ioctl_handler(struct net *net, void __user *arg)  
      2. {  
      3.     int err;  
      4.     struct brcm_ioctl_args args;  
      5.     struct net_device *dev = NULL;  
      6.   
      7.     if (copy_from_user(&args, arg, sizeof(struct brcm_ioctl_args)))  
      8.         return -EFAULT;  
      9.   
      10.     /* Null terminate this sucker, just in case. */  
      11.     args.device1[23] = 0;  
      12.     args.u.device2[23] = 0;  
      13.   
      14.     rtnl_lock();  
      15.   
      16.     switch (args.cmd) {  
      17.     case ADD_BRCM_CMD:  
      18.     case DEL_BRCM_CMD:  
      19.         err = -ENODEV;  
      20.         dev = __dev_get_by_name(net, args.device1);  
      21.         if (!dev)  
      22.             goto out;  
      23.   
      24.         err = -EINVAL;  
      25.         if (args.cmd != ADD_BRCM_CMD && !is_brcm_dev(dev))  
      26.             goto out;  
      27.     }  
      28.   
      29.     switch (args.cmd) {  
      30.     case ADD_BRCM_CMD:  
      31.         err = -EPERM;  
      32.         if (!capable(CAP_NET_ADMIN))  
      33.             break;  
      34.         err = register_brcm_dev(dev, args.u.port);  
      35.         break;  
      36.   
      37.     case DEL_BRCM_CMD:  
      38.         err = -EPERM;  
      39.         if (!capable(CAP_NET_ADMIN))  
      40.             break;  
      41.         unregister_brcm_dev(dev, NULL);  
      42.         err = 0;  
      43.         break;  
      44.           
      45.     default:  
      46.         err = -EOPNOTSUPP;  
      47.         break;  
      48.     }  
      49. out:  
      50.     rtnl_unlock();  
      51.     return err;  
      52. }  

              這些是brcm協(xié)議模塊的主體部分了,當然它還不完整,在下篇中繼續(xù)完成brcm協(xié)議的添加,為它完善一些細節(jié):proc文件系統(tǒng), notifier機制等等,以及內核Makefile的編寫,當然還有協(xié)議的測試。相關源碼在下篇中打包上傳。

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

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多