AF_PACKET套接字解密

 原文出处:

http://blog.chinaunix.net/uid-22362479-id-3220107.html

http://blog.chinaunix.net/uid-22362479-id-3220136.html

 

使用socket(AF_PACKET, SOCK_RAW, ETH_P_ALL)创建的套接字到底为何于众不同,今日追踪了一下。使用Linux 3.2.5版内核

net/socket.c

点击(此处)折叠或打开

  1. SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)

  2. {

  3. ......

  4.     retval = sock_create(family,  type, protocol, &sock);

  5. ......

  6. }

点击(此处)折叠或打开

  1. int sock_create(int family, int type, int protocol, struct socket **res)

  2. {

  3.     return __sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0);

  4. }

点击(此处)折叠或打开

  1. int __sock_create(struct net *net, int family, int type, int protocol,

  2.  struct socket **res, int kern)

  3. {

  4. ......

  5.     pf = rcu_dereference(net_families[family]);

  6. ......

  7.    err = pf->create(net, sock, protocol, kern);

  8. ......

  9. }

点击(此处)折叠或打开

  1. static const struct net_proto_family __rcu *net_families[NPROTO] __read_mostly;


  2. int sock_register(const struct net_proto_family *ops)

  3. {

  4. ......

  5.     if (rcu_dereference_protected(net_families[ops->family],

  6.                       lockdep_is_held(&net_family_lock)))

  7.         err = -EEXIST;

  8.     else {

  9. rcu_assign_pointer(net_families[ops->family], ops);

  10.         err = 0;

  11.     }

  12. ......

  13. }


net/packet/af_packet.c

点击(此处)折叠或打开

  1. static const struct net_proto_family packet_family_ops = {

  2.     .family =    PF_PACKET,

  3.     .create =    packet_create,

  4.     .owner    =    THIS_MODULE,

  5. };


  6. static int __init packet_init(void)

  7. {

  8. ......

  9.     sock_register(&packet_family_ops);

  10. ......

  11. }


  12. module_init(packet_init);

点击(此处)折叠或打开

  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 */

  7. ......

  8.     sk = sk_alloc(net, PF_PACKET, GFP_KERNEL, &packet_proto);

  9. ......

  10.     sock->ops = &packet_ops;

  11. ......

  12.     po = pkt_sk(sk);
        sk->sk_family = PF_PACKET;

  13.     po->num = proto;

  14. ......

  15.     po->prot_hook.func = packet_rcv;

  16. ......

  17.     po->prot_hook.af_packet_priv = sk;

  18.     if (proto) {

  19. po->prot_hook.type = proto;

  20. register_prot_hook(sk);

  21.     }

  22. ......

  23. }


AF_PACKET套接字的功能来源于prot_hook,其本身是struct packet_type类型:
1:type成员设定为了socket()传递的参数(这里是ETH_P_ALL)
2:过滤得到的包的处理函数保存于func成员,这里被设定为 packet_rcv()
3:dev成员用于对net_device的过滤,可以在bind()中指定


 点击(此处)折叠或打开

  1. static int packet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)

  2. {

  3. ......

  4. if(sll->sll_ifindex){

  5. err =-ENODEV;

  6.         dev = dev_get_by_index(sock_net(sk), sll->sll_ifindex);

  7. if(dev ==NULL)

  8.             goto out;

  9. }

  10. err =packet_do_bind(sk, dev, sll->sll_protocol ?: pkt_sk(sk)->num);

  11. ......

  12. }

点击(此处)折叠或打开

  1. static intpacket_do_bind(struct sock *sk, struct net_device *dev, __be16 protocol)

  2. {

  3.     struct packet_sock *po = pkt_sk(sk);

  4. ......

  5.     po->prot_hook.dev = dev;


  6.     po->ifindex = dev ? dev->ifindex : 0;

  7. ......

  8. if(!dev ||(dev->flags & IFF_UP)){

  9. register_prot_hook(sk);

  10. ......

  11. }


可见在bind()中若指定了绑定的net_device同样会触发prot_hook的注册动作。


 点击(此处)折叠或打开

  1. static void register_prot_hook(struct sock *sk)

  2. {

  3.     struct packet_sock *po = pkt_sk(sk);

  4. if(!po->running){

  5. if(po->fanout)

  6.             __fanout_link(sk, po);

  7. else

  8.             dev_add_pack(&po->prot_hook);

  9.         sock_hold(sk);

  10.         po->running = 1;

  11. }

  12. }


net/core/dev.c

点击(此处)折叠或打开

  1. void dev_add_pack(struct packet_type *pt)

  2. {

  3.     struct list_head *head =ptype_head(pt);

  4.     spin_lock(&ptype_lock);

  5.     list_add_rcu(&pt->list, head);

  6.     spin_unlock(&ptype_lock);

  7. }

点击(此处)折叠或打开

  1. static struct list_head ptype_all __read_mostly;    /* Taps */

  2. static inline struct list_head *ptype_head(const struct packet_type *pt)

  3. {

  4. if(pt->type == htons(ETH_P_ALL))

  5.         return &ptype_all;

  6. else

  7.         return &ptype_base[ntohs(pt->type)& PTYPE_HASH_MASK];

  8. }


历尽千辛万苦,终于知道AF_PACKET套接字把自己的prot_hook挂到了ptype_all链表上或ptype_base链表上。当AF_PACKET套接字注册了prot_hook后,怎样进行监听呢,先来看发送:

当协议栈准备将数据交给net_device发送时,它将调用dev_queue_xmit():

点击(此处)折叠或打开

  1. int dev_queue_xmit(struct sk_buff *skb)

  2. {

  3.     struct net_device *dev = skb->dev;

  4. ......

  5.                     rc = dev_hard_start_xmit(skb, dev, txq);

  6. ......

  7. }


点击(此处)折叠或打开

  1. int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,

  2.             struct netdev_queue *txq)

  3. {

  4. ......

  5. if(!list_empty(&ptype_all))

  6.             dev_queue_xmit_nit(skb, dev);

  7. ......

  8. }


由于AF_PACKET套接字注册了prot_hook,将导致dev_queue_xmit_nit()被调用:

 点击(此处)折叠或打开

  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. ......

  23. }

  24. }

  25. if(pt_prev)

  26.         pt_prev->func(skb2, skb->dev, pt_prev, skb->dev);

  27.     rcu_read_unlock();

  28. }

在遍历ptype_all链表时,这里有几点需要着重说明:

1:对于发送的包过滤条件有:
    1). net_device是否是prot_hook指定的dev(NULL代表全部匹配)。
    2). ptype->af_packet_priv在packet_creat()中被设定为自己,故自己发送的包不会被监听。

2:遍历ptype_all时,第一次会复制skb;只有ptype_all中不止1个entry时,将调用deliver_skb()。

3:当退出遍历时,将调用prot_hook的func成员,即packet_rcv()。

4: deliver_skb()仅仅是在调用prot_hook的func成员前增加skb的引用计算数。

 点击(此处)折叠或打开

  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. ......

  5.     __skb_queue_tail(&sk->sk_receive_queue, skb);

  6. ......

  7. }

经过packet_rcv(),发送的数据包被加入到了AF_PACKET套接字的接收队列,等待我们的读取。。。一切就是这么简单!

 

你可能感兴趣的:(linux,protocol,family)