Chinaunix首页 | 论坛 | 博客
  • 博客访问: 83489
  • 博文数量: 15
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 210
  • 用 户 组: 普通用户
  • 注册时间: 2014-01-05 15:27
文章分类

全部博文(15)

文章存档

2014年(15)

我的朋友

分类: LINUX

2014-04-19 18:21:16

SOCK_RAW给了用户更大的主动性,可以自己构造L4甚至L3的头,直接和内核进行交互,略过上层的协议栈,如ping命令:
  1. socket(PF_INET, SOCK_RAW, IPPROTO_ICMP) = 3
看下面这个例子:
  1. int sockfd;
  2. struct sockaddr_in addr;
  3. char sendbuf[2048];
  4. char recvbuf[2048];
  5. int datalen = 56;

  6. unsigned short my_cksum(unsigned short *data, int len) { //校验算法
  7.     int result = 0;
  8.     int i;
  9.     for(i=0; i<len/2; i++) {
  10.         result += *data;
  11.         data++;
  12.     }
  13.     while(result >> 16)result = (result&0xffff) + (result>>16);
  14.     return ~result;
  15. }
  16. void send_icmp() {
  17.     struct icmp* icmp = (struct icmp*)sendbuf;
  18.     int len = 8+datalen;//长度为L4的头加上负载
  19.     icmp->icmp_type = ICMP_ECHO;
  20.     icmp->icmp_code = 0;
  21.     icmp->icmp_cksum = my_cksum((unsigned short*)icmp, len); //校验值,如果校验值错误的话,不影响发送,但是不会有响应报文
  22.     int retval = sendto(sockfd, sendbuf, len, 0, (struct sockaddr*)&addr,sizeof(addr));
  23.     printf("send %d\n",retval);
  24. }
  25. void recv_icmp() {
  26.     for(;;) {
  27.         int len = recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, 0, 0);
  28.         printf("recv len=%d\n",len); //接收到的数据为L3+L4+负载
  29.     }
  30. }

  31. int main(int argc, char **argv)
  32. {

  33.     int ret;
  34.     sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

  35.     memset(&addr, 0, sizeof(addr));
  36.     addr.sin_family = AF_INET;
  37.     ret=inet_pton(AF_INET, argv[1], &addr.sin_addr);  //初始化目的地址

  38.     send_icmp();
  39.     recv_icmp();
  40.     return 0;
  41. }
运行一次输出结果如下:
  1. root@pavel:/home/pavel# ./3 192.168.1.1
  2. send 64
  3. recv len=84
tcpdump抓的数据如下:
  1. 21:42:22.501278 IP 192.168.1.105 > 192.168.1.1: ICMP echo request, id 0, seq 0, length 64
  2.     0x0000: 0023 cd5b ead6 8056 f2db 2f7b 0800 4500
  3.     0x0010: 0054 18a5 4000 4001 9e49 c0a8 0169 c0a8
  4.     0x0020: 0101 0800 f7ff 0000 0000 0000 0000 0000
  5.     0x0030: 0000 0000 0000 0000 0000 0000 0000 0000
  6.     0x0040: 0000 0000 0000 0000 0000 0000 0000 0000
  7.     0x0050: 0000 0000 0000 0000 0000 0000 0000 0000
  8.     0x0060: 0000
  9. 21:42:22.731070 IP 192.168.1.1 > 192.168.1.105: ICMP echo reply, id 0, seq 0, length 64
  10.     0x0000: 8056 f2db 2f7b 0023 cd5b ead6 0800 4500
  11.     0x0010: 0054 8460 4000 4001 328e c0a8 0101 c0a8
  12.     0x0020: 0169 0000 ffff 0000 0000 0000 0000 0000
  13.     0x0030: 0000 0000 0000 0000 0000 0000 0000 0000
  14.     0x0040: 0000 0000 0000 0000 0000 0000 0000 0000
  15.     0x0050: 0000 0000 0000 0000 0000 0000 0000 0000
  16.     0x0060: 0000
可以看到上述代码基本完成了类似ping的功能
SOCK_RAW除了用户可以构造L4的头,甚至L3的头,还可以用开监听接收报文。同样的刚才的那个程序,如果单独运行的话,只发送一次,接收打印一次,然后就等待,如果此时在开另外一个窗口运行:ping 192.168.1.1的话,刚才的程序同样可以接收到响应这个ping命令的报文。
这个主要是在函数ip_local_deliver_finish中实现的,该函数在调用真正的L4层注册的协议前调用了函数raw_local_deliver:
  1. int raw_local_deliver(struct sk_buff *skb, int protocol)
  2. {
  3.     int hash;
  4.     struct sock *raw_sk;

  5.     hash = protocol & (RAW_HTABLE_SIZE - 1);
  6.     raw_sk = sk_head(&raw_v4_hashinfo.ht[hash]);

  7.     /* If there maybe a raw socket we must check - if not we
  8.      * don't care less
  9.      */
  10.     if (raw_sk && !raw_v4_input(skb, ip_hdr(skb), hash))
  11.         raw_sk = NULL;

  12.     return raw_sk != NULL;

  13. }
  1. static int raw_v4_input(struct sk_buff *skb, const struct iphdr *iph, int hash)
  2. {
  3.     struct sock *sk;
  4.     struct hlist_head *head;
  5.     int delivered = 0;
  6.     struct net *net;

  7.     read_lock(&raw_v4_hashinfo.lock);
  8.     head = &raw_v4_hashinfo.ht[hash];
  9.     if (hlist_empty(head))
  10.         goto out;

  11.     net = dev_net(skb->dev);
  12.     sk = __raw_v4_lookup(net, __sk_head(head), iph->protocol,
  13.                  iph->saddr, iph->daddr,
  14.                  skb->dev->ifindex);

  15.     while (sk) {
  16.         delivered = 1;
  17.         if (iph->protocol != IPPROTO_ICMP || !icmp_filter(sk, skb)) {
  18.             struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);

  19.             /* Not releasing hash */
  20.             if (clone)
  21.                 raw_rcv(sk, clone);
  22.         }
  23.         sk = __raw_v4_lookup(net, sk_next(sk), iph->protocol,  //循环调用,直到遍历完,因此如果多开几个测试程序,然后运行ping命令的话
  24.                      iph->saddr, iph->daddr,                    //所有的测试程序都能接收到reply报文
  25.                      skb->dev->ifindex);
  26.     }
  27. out:
  28.     read_unlock(&raw_v4_hashinfo.lock);
  29.     return delivered;
  30. }
符合条件的每一个sock都会调用raw_rcv:
  1. int raw_rcv(struct sock *sk, struct sk_buff *skb)
  2. {
  3.     if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) {
  4.         atomic_inc(&sk->sk_drops);
  5.         kfree_skb(skb);
  6.         return NET_RX_DROP;
  7.     }
  8.     nf_reset(skb);
  9.     
  10.     skb_push(skb, skb->data - skb_network_header(skb));//从这里可以看到接收到的报文是包含L3的头的,但是不包含L2的头
  11.                                                        //要获取L2的头的话,需要使用PF_PACKET
  12.     raw_rcv_skb(sk, skb);
  13.     return 0;
  14. }
系统中raw相关的信息可以通过proc查到,下面这个是开了两个测试程序以及一个ping命令后的结果:
  1. pavel@pavel:~$ cat /proc/net/raw
  2.   sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode ref pointer drops
  3.    1: 00000000:0001 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 200182 2 0000000000000000 0
  4.    1: 00000000:0001 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 190496 2 0000000000000000 0
  5.    1: 00000000:0001 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 180978 2 0000000000000000 0

SOCK_RAW同样可以从驱动层直接获取报文,因此可以用于网络包的分析,如tcpdump命令,不过需要和PF_PACKET一起:
  1. socket(PF_PACKET, SOCK_RAW, 768) = 3 //ETH_P_ALL
先看一下tcpdump相关的内容,看tcpdump是如何实现的,试着写一个类似的程序:
PF_PACKET的初始化如下:
  1. static int __init packet_init(void)
  2. {
  3.     int rc = proto_register(&packet_proto, 0);
  4.     
  5.     if (rc != 0)
  6.         goto out;

  7.     sock_register(&packet_family_ops);
  8.     register_pernet_subsys(&packet_net_ops);
  9.     register_netdevice_notifier(&packet_netdev_notifier);
  10. out:
  11.     return rc;
  12. }
相应的数据结构:
  1. static struct proto packet_proto = {
  2.     .name = "PACKET",
  3.     .owner = THIS_MODULE,
  4.     .obj_size = sizeof(struct packet_sock),
  5. };
  6. static const struct net_proto_family packet_family_ops = {
  7.     .family = PF_PACKET,
  8.     .create = packet_create,
  9.     .owner = THIS_MODULE,
  10. };
  11. static struct pernet_operations packet_net_ops = {
  12.     .init = packet_net_init,
  13.     .exit = packet_net_exit,
  14. };
  1. static const struct proto_ops packet_ops = {
  2.     .family = PF_PACKET,
  3.     .owner = THIS_MODULE,
  4.     .release = packet_release,
  5.     .bind = packet_bind,
  6.     .connect = sock_no_connect,
  7.     .socketpair = sock_no_socketpair,
  8.     .accept = sock_no_accept,
  9.     .getname = packet_getname,
  10.     .poll = packet_poll,
  11.     .ioctl = packet_ioctl,
  12.     .listen = sock_no_listen,
  13.     .shutdown = sock_no_shutdown,
  14.     .setsockopt = packet_setsockopt,
  15.     .getsockopt = packet_getsockopt,
  16.     .sendmsg = packet_sendmsg,
  17.     .recvmsg = packet_recvmsg,
  18.     .mmap = packet_mmap,
  19.     .sendpage = sock_no_sendpage,
  20. };
socket系统调用分配struct socket数据结构,赋值分量:
  1. sock->type = type; //SOCK_RAW
然后调用packet_create,初始化相应的分量:
  1. static int packet_create(struct net *net, struct socket *sock, int protocol,
  2.              int kern)
  3. {
  4.     struct sock *sk;
  5.     struct packet_sock *po;
  6.     __be16 proto = (__force __be16)protocol; /* weird, but documented */  //ETH_P_ALL
  7.     int err;

  8.     if (!capable(CAP_NET_RAW))
  9.         return -EPERM;
  10.     if (sock->type != SOCK_DGRAM && sock->type != SOCK_RAW &&
  11.         sock->type != SOCK_PACKET)
  12.         return -ESOCKTNOSUPPORT;

  13.     sock->state = SS_UNCONNECTED;

  14.     err = -ENOBUFS;
  15.     sk = sk_alloc(net, PF_PACKET, GFP_KERNEL, &packet_proto);
  16.     if (sk == NULL)
  17.         goto out;

  18.     sock->ops = &packet_ops;
  19.     if (sock->type == SOCK_PACKET)  //SOCK_RAW
  20.         sock->ops = &packet_ops_spkt;

  21.     sock_init_data(sock, sk);

  22.     po = pkt_sk(sk);
  23.     sk->sk_family = PF_PACKET;
  24.     po->num = proto;

  25.     sk->sk_destruct = packet_sock_destruct;
  26.     sk_refcnt_debug_inc(sk);

  27.     /*
  28.      * Attach a protocol block
  29.      */

  30.     spin_lock_init(&po->bind_lock);
  31.     mutex_init(&po->pg_vec_lock);

  32.     po->prot_hook.func = packet_rcv;

  33.     if (sock->type == SOCK_PACKET)
  34.         po->prot_hook.func = packet_rcv_spkt;

  35.     po->prot_hook.af_packet_priv = sk;

  36.     if (proto) {
  37.         po->prot_hook.type = proto;
  38.         register_prot_hook(sk);  //注册当前packet_type到ptype_all链表中,这样接收的时候可以触发相应的接收函数
  39.     }

  40.     spin_lock_bh(&net->packet.sklist_lock);
  41.     sk_add_node_rcu(sk, &net->packet.sklist);
  42.     sock_prot_inuse_add(net, &packet_proto, 1);
  43.     spin_unlock_bh(&net->packet.sklist_lock);

  44.     return 0;
  45. out:
  46.     return err;
  47. }
利用上述的socket类型,很容易获取到一个完整的数据包:
  1. int main(int argc, char **argv) {
  2.     int sock, n;
  3.     char buffer[LENGTH];
  4.     unsigned char *iphead, *ethhead;

  5.     if ( (sock=socket(PF_PACKET, SOCK_RAW,htons(ETH_P_ALL)))<0) {
  6.         perror("socket");
  7.         return -1;
  8.     }
  9.     
  10.     while (1) {
  11.         int i;
  12.         n = recvfrom(sock,buffer,LENGTH,0,NULL,NULL);
  13.         printf("%d bytes read \n",n);
  14.         for(i=0;i<n;i++)
  15.             printf("%x ",buffer[i]);
  16.         printf("\n");
  17.     }
  18. }
其中一次的接收数据如下:
  1. 98 bytes read //64+L3+L2
  2. ffffff80 56 fffffff2 ffffffdb 2f 7b 0 23 ffffffcd 5b ffffffea ffffffd6 8 0 45 0 0 54 78 ffffffea 40 0 40 1 3e 4 ffffffc0 ffffffa8 1 1 ffffffc0 ffffffa8 1 69 0 0 3b 33 24 30 0 1 40 38 52 53 0 0 0 0 4d 3d 2 0 0 0 0 0 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37
对应的tcpdump输出如下:
  1. 16:48:00.177672 IP 192.168.1.1 > 192.168.1.105: ICMP echo reply, id 9264, seq 1, length 64
  2.     0x0000: 8056 f2db 2f7b 0023 cd5b ead6 0800 4500
  3.     0x0010: 0054 78ea 4000 4001 3e04 c0a8 0101 c0a8
  4.     0x0020: 0169 0000 3b33 2430 0001 4038 5253 0000
  5.     0x0030: 0000 4d3d 0200 0000 0000 1011 1213 1415
  6.     0x0040: 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425
  7.     0x0050: 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435
  8.     0x0060: 3637
可以看到两者完全对应。

我们知道在函数__netif_receive_skb中会把根据收到的包的类型,调用不同的L3注册的函数,其中涉及ptype_all和ptype_base两个数据结构。
默认情况下如下:
  1. Type Device Function
  2. 0800 ip_rcv
  3. 0011 llc_rcv [llc]
  4. 0004 llc_rcv [llc]
  5. 0806 arp_rcv
  6. 86dd ipv6_rcv
运行测试程序如下:
  1. Type Device Function
  2. ALL packet_rcv  //增加了这一样,意味着所有的包都会增加调用这个函数的一步,register_prot_hook函数完成
  3. 0800 ip_rcv
  4. 0011 llc_rcv [llc]
  5. 0004 llc_rcv [llc]
  6. 0806 arp_rcv
  7. 86dd ipv6_rcv
tcpdump运行后的输出如下:
  1. Type Device      Function
  2. ALL eth0 tpacket_rcv  //tcpdump增加了PACKET_RX_RING的设置
  3. 0800 ip_rcv
  4. 0011 llc_rcv [llc]
  5. 0004 llc_rcv [llc]
  6. 0806 arp_rcv
  1. static int packet_rcv(struct sk_buff *skb, struct net_device *dev,
  2.               struct packet_type *pt, struct net_device *orig_dev)
  3. {
  4.     struct sock *sk;
  5.     struct sockaddr_ll *sll;
  6.     struct packet_sock *po;
  7.     u8 *skb_head = skb->data;
  8.     int skb_len = skb->len;
  9.     unsigned int snaplen, res;

  10.     if (skb->pkt_type == PACKET_LOOPBACK)
  11.         goto drop;
  12.     
  13.     sk = pt->af_packet_priv;  //packet_create的时候赋值
  14.     po = pkt_sk(sk);
  15.     
  16.     if (!net_eq(dev_net(dev), sock_net(sk)))
  17.         goto drop;

  18.     skb->dev = dev;

  19.     if (dev->header_ops) {
  20.         /* The device has an explicit notion of ll header,
  21.          * exported to higher levels.
  22.          *
  23.          * Otherwise, the device hides details of its frame
  24.          * structure, so that corresponding packet head is
  25.          * never delivered to user.
  26.          */
  27.         if (sk->sk_type != SOCK_DGRAM)  //L2头在驱动程序中pull了,因此这里push,使得用于可以接收到L2头的数据
  28.             skb_push(skb, skb->data - skb_mac_header(skb));
  29.         else if (skb->pkt_type == PACKET_OUTGOING) {
  30.             /* Special case: outgoing packets have ll header at head */
  31.             skb_pull(skb, skb_network_offset(skb));
  32.         }
  33.     }
  34.     
  35.     snaplen = skb->len;

  36.     res = run_filter(skb, sk, snaplen);
  37.     if (!res)
  38.         goto drop_n_restore;
  39.     if (snaplen > res)
  40.         snaplen = res;

  41.     if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >=
  42.         (unsigned)sk->sk_rcvbuf)
  43.         goto drop_n_acct;

  44.     if (skb_shared(skb)) {
  45.         struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC);
  46.         if (nskb == NULL)
  47.             goto drop_n_acct;

  48.         if (skb_head != skb->data) {
  49.             skb->data = skb_head;
  50.             skb->len = skb_len;
  51.         }
  52.         kfree_skb(skb);
  53.         skb = nskb;
  54.     }

  55.     BUILD_BUG_ON(sizeof(*PACKET_SKB_CB(skb)) + MAX_ADDR_LEN - 8 >
  56.              sizeof(skb->cb));

  57.     sll = &PACKET_SKB_CB(skb)->sa.ll;
  58.     sll->sll_family = AF_PACKET;
  59.     sll->sll_hatype = dev->type;
  60.     sll->sll_protocol = skb->protocol;
  61.     sll->sll_pkttype = skb->pkt_type;
  62.     if (unlikely(po->origdev))
  63.         sll->sll_ifindex = orig_dev->ifindex;
  64.     else
  65.         sll->sll_ifindex = dev->ifindex;

  66.     sll->sll_halen = dev_parse_header(skb, sll->sll_addr);

  67.     PACKET_SKB_CB(skb)->origlen = skb->len;

  68.     if (pskb_trim(skb, snaplen))
  69.         goto drop_n_acct;

  70.     skb_set_owner_r(skb, sk);
  71.     skb->dev = NULL;
  72.     skb_dst_drop(skb);

  73.     /* drop conntrack reference */
  74.     nf_reset(skb);

  75.     spin_lock(&sk->sk_receive_queue.lock);
  76.     po->stats.tp_packets++;
  77.     skb->dropcount = atomic_read(&sk->sk_drops);
  78.     __skb_queue_tail(&sk->sk_receive_queue, skb);  //和其他socket类型不同是不处理L3以及L4的头,直接把原始数据传给用户态
  79.     spin_unlock(&sk->sk_receive_queue.lock);
  80.     sk->sk_data_ready(sk, skb->len);
  81.     return 0;

  82. drop_n_acct:
  83.     spin_lock(&sk->sk_receive_queue.lock);
  84.     po->stats.tp_drops++;
  85.     atomic_inc(&sk->sk_drops);
  86.     spin_unlock(&sk->sk_receive_queue.lock);

  87. drop_n_restore:
  88.     if (skb_head != skb->data && skb_shared(skb)) {
  89.         skb->data = skb_head;
  90.         skb->len = skb_len;
  91.     }
  92. drop:
  93.     consume_skb(skb);
  94.     return 0;
  95. }
总结:
1)tcpdump的原理是利用PF_PACKET类型的socket类型,在ptype_all链表中增加处理函数,使得所有进来的包都能捕捉到
2)PF_PACKET对应的收包函数不处理L3和L4的头,因此用户可以得到完全原始的数据,包括L2、L3以及L4的头

混杂模式,默认情况下网卡只是接收发给自己或者组播、广播的报文,对于发给其他MAC地址的报文,硬件上就直接丢弃了。为了更好的监听网络信息,需要获取这些报文,这个可以通过把网卡设成混杂模式实现。
一般运行tcpdump后会打印:device eth* entered promiscuous mode
用户态程序通过ioctl可以实现:
  1.     struct ifreq ethreq;
  2.     strncpy(ethreq.ifr_name,"eth1",IFNAMSIZ);
  3.     if (ioctl(sock,SIOCGIFFLAGS,&ethreq)==-1) {
  4.         perror("ioctl");
  5.         close(sock);
  6.         return -1;
  7.     }
  8.     ethreq.ifr_flags |= IFF_PROMISC;
  9.     if (ioctl(sock,SIOCSIFFLAGS,&ethreq)==-1) {
  10.         perror("ioctl");
  11.         close(sock);
  12.         return -1;
  13.     }
内核的调用流程如下:
  1. [ 144.165484] [<ffffffff815e0af7>] __dev_set_rx_mode+0x57/0xa0  
  2. [ 144.165486] [<ffffffff815e0b66>] dev_set_rx_mode+0x26/0x40
  3. [ 144.165489] [<ffffffff815e0d27>] dev_set_promiscuity+0x37/0x50
  4. [ 144.165491] [<ffffffff815e0f01>] __dev_change_flags+0xd1/0x170
  5. [ 144.165494] [<ffffffff815e104d>] dev_change_flags+0x1d/0x60
  6. [ 144.165499] [<ffffffff81644463>] devinet_ioctl+0x693/0x790
  7. [ 144.165502] [<ffffffff8164523d>] inet_ioctl+0x6d/0xa0
  8. [ 144.165504] [<ffffffff8169c6f1>] packet_ioctl+0xc1/0x150
  9. [ 144.165508] [<ffffffff815c4ff5>] sock_do_ioctl+0x25/0x50
  10. [ 144.165510] [<ffffffff815c5438>] sock_ioctl+0x1c8/0x280
  11. [ 144.165513] [<ffffffff811b75c5>] do_vfs_ioctl+0x2e5/0x4d0
__dev_set_rx_mode会调用驱动的ndo_set_rx_mode或者ndo_set_multicast_list函数,r8169驱动注册的钩子函数如下:
  1. static void rtl_set_rx_mode(struct net_device *dev)
  2. {
  3.     struct rtl8169_private *tp = netdev_priv(dev);
  4.     void __iomem *ioaddr = tp->mmio_addr;
  5.     unsigned long flags;
  6.     u32 mc_filter[2]; /* Multicast hash filter */
  7.     int rx_mode;
  8.     u32 tmp = 0;

  9.     if (dev->flags & IFF_PROMISC) {
  10.         /* Unconditionally log net taps. */
  11.         netif_notice(tp, link, dev, "Promiscuous mode enabled\n");
  12.         rx_mode =
  13.             AcceptBroadcast | AcceptMulticast | AcceptMyPhys |
  14.             AcceptAllPhys;  //混杂模式下接收所有报文
  15.         mc_filter[1] = mc_filter[0] = 0xffffffff;
  16.     } else if ((netdev_mc_count(dev) > multicast_filter_limit) ||
  17.            (dev->flags & IFF_ALLMULTI)) {
  18.         /* Too many to filter perfectly -- accept all multicasts. */
  19.         rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
  20.         mc_filter[1] = mc_filter[0] = 0xffffffff;
  21.     } else {
  22.         struct netdev_hw_addr *ha;

  23.         rx_mode = AcceptBroadcast | AcceptMyPhys;
  24.         mc_filter[1] = mc_filter[0] = 0;
  25.         netdev_for_each_mc_addr(ha, dev) {
  26.             int bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26;
  27.             mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
  28.             rx_mode |= AcceptMulticast;
  29.         }
  30.     }

  31.     spin_lock_irqsave(&tp->lock, flags);

  32.     tmp = (RTL_R32(RxConfig) & ~RX_CONFIG_ACCEPT_MASK) | rx_mode;

  33.     if (tp->mac_version > RTL_GIGA_MAC_VER_06) {
  34.         u32 data = mc_filter[0];

  35.         mc_filter[0] = swab32(mc_filter[1]);
  36.         mc_filter[1] = swab32(data);
  37.     }
  38.     RTL_W32(MAR0 + 4, mc_filter[1]);
  39.     RTL_W32(MAR0 + 0, mc_filter[0]);

  40.     RTL_W32(RxConfig, tmp);

  41.     spin_unlock_irqrestore(&tp->lock, flags);
  42. }
其中变量定义如下:
  1.     AcceptErr = 0x20,
  2.     AcceptRunt = 0x10,
  3.     AcceptBroadcast = 0x08,
  4.     AcceptMulticast = 0x04,
  5.     AcceptMyPhys = 0x02,
  6.     AcceptAllPhys = 0x01,
对照芯片手册,比较清楚的可以知道上述的含义,就是写相关的硬件寄存器,让其接收所有的报文。

看一下发送的数据包是如何捕捉到的:
  1. int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
  2.             struct netdev_queue *txq)
  3. {
  4.     const struct net_device_ops *ops = dev->netdev_ops;
  5.     int rc = NETDEV_TX_OK;
  6.     unsigned int skb_len;

  7.     if (likely(!skb->next)) {
  8.         u32 features;

  9.         /*
  10.          * If device doesn't need skb->dst, release it right now while
  11.          * its hot in this cpu cache
  12.          */
  13.         if (dev->priv_flags & IFF_XMIT_DST_RELEASE)
  14.             skb_dst_drop(skb);

  15.         if (!list_empty(&ptype_all))
  16.             dev_queue_xmit_nit(skb, dev);  //调用钩子函数
  17. ...
  18. }
  1. static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev)
  2. {
  3.     struct packet_type *ptype;
  4.     struct sk_buff *skb2 = NULL;
  5.     struct packet_type *pt_prev = NULL;

  6.     rcu_read_lock();
  7.     list_for_each_entry_rcu(ptype, &ptype_all, list) {
  8.         /* Never send packets back to the socket
  9.          * they originated from - MvS (miquels@drinkel.ow.org)
  10.          */
  11.         if ((ptype->dev == dev || !ptype->dev) &&
  12.             (ptype->af_packet_priv == NULL ||
  13.              (struct sock *)ptype->af_packet_priv != skb->sk)) {
  14.             if (pt_prev) {
  15.                 deliver_skb(skb2, pt_prev, skb->dev);
  16.                 pt_prev = ptype;
  17.                 continue;
  18.             }

  19.             skb2 = skb_clone(skb, GFP_ATOMIC);
  20.             if (!skb2)
  21.                 break;

  22.             net_timestamp_set(skb2);

  23.             /* skb->nh should be correctly
  24.                set by sender, so that the second statement is
  25.                just protection against buggy protocols.
  26.              */
  27.             skb_reset_mac_header(skb2);

  28.             if (skb_network_header(skb2) < skb2->data ||
  29.                 skb2->network_header > skb2->tail) {
  30.                 if (net_ratelimit())
  31.                     printk(KERN_CRIT "protocol %04x is "
  32.                            "buggy, dev %s\n",
  33.                            ntohs(skb2->protocol),
  34.                            dev->name);
  35.                 skb_reset_network_header(skb2);
  36.             }
  37.             skb2->transport_header = skb2->network_header;
  38.             skb2->pkt_type = PACKET_OUTGOING;
  39.             pt_prev = ptype;
  40.         }
  41.     }
  42.     if (pt_prev)
  43.         pt_prev->func(skb2, skb->dev, pt_prev, skb->dev)
  44.     rcu_read_unlock();
  45. }
发送的流程调用的是和接收流程一样的函数。














阅读(9995) | 评论(0) | 转发(0) |
0

上一篇:NAPI

下一篇:没有了

给主人留下些什么吧!~~