Chinaunix首页 | 论坛 | 博客
  • 博客访问: 179922
  • 博文数量: 108
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 1065
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-29 08:56
文章分类

全部博文(108)

文章存档

2011年(11)

2010年(46)

2009年(29)

2008年(22)

我的朋友

分类: LINUX

2010-07-14 11:40:39

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define MPF_PACKET AF_ROSE
/*
   Assumptions:
   - if device has no dev->hard_header routine, it adds and removes ll header
     inside itself. In this case ll header is invisible outside of device,
     but higher levels still should reserve dev->hard_header_len.
     Some devices are enough clever to reallocate skb, when header
     will not fit to reserved space (tunnel), another ones are silly
     (PPP).
   - packet socket receives packets with pulled ll header,
     so that SOCK_RAW should push it back.

On receive:
-----------

Incoming, dev->hard_header!=NULL
   mac_header -> ll header
   data       -> data

Outgoing, dev->hard_header!=NULL
   mac_header -> ll header
   data       -> ll header

Incoming, dev->hard_header==NULL
   mac_header -> UNKNOWN position. It is very likely, that it points to ll
         header.  PPP makes it, that is wrong, because introduce
         assymetry between rx and tx paths.
   data       -> data

Outgoing, dev->hard_header==NULL
   mac_header -> data. ll header is still not built!
   data       -> data

Resume
  If dev->hard_header==NULL we are unlikely to restore sensible ll header.

On transmit:
------------

dev->hard_header != NULL
   mac_header -> ll header
   data       -> ll header

dev->hard_header == NULL (ll header is added by device, we cannot control it)
   mac_header -> data
   data       -> data

   We should set nh.raw on output to correct posistion,
   packet classifier depends on it.
 */

/* Private packet socket structures. */

struct packet_mclist
{
    struct packet_mclist *next;
    int ifindex;
    int count;
    unsigned short type;
    unsigned short alen;
    unsigned char addr[MAX_ADDR_LEN];
};
/* identical to struct packet_mreq except it has
 * a longer address field.
 */
struct packet_mreq_max
{
    int mr_ifindex;
    unsigned short mr_type;
    unsigned short mr_alen;
    unsigned char mr_address[MAX_ADDR_LEN];
};

static void packet_flush_mclist (struct sock *sk);

struct packet_sock
{
    /* struct sock has to be the first member of packet_sock */
    struct sock sk;
    struct tpacket_stats stats;

    struct packet_type prot_hook;
    spinlock_t bind_lock;
    struct mutex pg_vec_lock;
    unsigned int running:1,        /* prot_hook is attached */
                 auxdata:1,
                 origdev:1;
    int    ifindex;                    /* bound device     */
    __be16 num;
    struct packet_mclist *mclist;

};

struct packet_skb_cb
{
    unsigned int origlen;
    union
    {
        struct sockaddr_pkt pkt;
        struct sockaddr_ll ll;
    } sa;
};

#define PACKET_SKB_CB(__skb)    ((struct packet_skb_cb *)((__skb)->cb))


struct netns_packet    packet;




static inline struct packet_sock *pkt_sk (struct sock *sk)
{
    return (struct packet_sock *) sk;
}

static void packet_sock_destruct (struct sock *sk)
{
    WARN_ON (atomic_read (&sk->sk_rmem_alloc));
    WARN_ON (atomic_read (&sk->sk_wmem_alloc));

    if (!sock_flag (sk, SOCK_DEAD))
    {
        printk ("Attempt to release alive packet socket: %p\n", sk);
        return;
    }

    sk_refcnt_debug_dec (sk);
}

static const struct proto_ops packet_ops;

/*SO_ATTACH_FILTER */
static inline unsigned int run_filter (struct sk_buff *skb, struct sock *sk,
    unsigned int res)
{
    struct sk_filter *filter;

    rcu_read_lock_bh ();
    filter = rcu_dereference (sk->sk_filter);
    if (filter != NULL)
        res = sk_run_filter (skb, filter->insns, filter->len);
    rcu_read_unlock_bh ();

    return res;
}

/*
   This function makes lazy skb cloning in hope that most of packets
   are discarded by BPF.

   Note tricky part: we DO mangle shared skb! skb->data, skb->len
   and skb->cb are mangled. It works because (and until) packets
   falling here are owned by current CPU. Output packets are cloned
   by dev_queue_xmit_nit(), input packets are processed by net_bh
   sequencially, so that if we return skb to original state on exit,
   we will not harm anyone.
 */
static int packet_rcv (struct sk_buff *skb, struct net_device *dev,
                         struct packet_type *pt, struct net_device *orig_dev)
{
    struct sock *sk;
    struct sockaddr_ll *sll;
    struct packet_sock *po;
    u8 *skb_head = skb->data;
    int skb_len = skb->len;
    unsigned int snaplen, res;

    if (skb->pkt_type == PACKET_LOOPBACK)
        goto drop;

    sk = pt->af_packet_priv;
    po = pkt_sk (sk);

    if (dev_net (dev) != sock_net (sk))
        goto drop;

    skb->dev = dev;

    if (dev->header_ops)
    {
        /* The device has an explicit notion of ll header,
           exported to higher levels.

           Otherwise, the device hides datails of it frame
           structure, so that corresponding packet head
           never delivered to user.
         */
        if (sk->sk_type != SOCK_DGRAM)
            skb_push (skb, skb->data - skb_mac_header (skb));
        else if (skb->pkt_type == PACKET_OUTGOING)
        {
            /* Special case: outgoing packets have ll header at head */
            skb_pull (skb, skb_network_offset (skb));
        }
    }

    snaplen = skb->len;
  
//SO_ATTACH_FILTER
    res = run_filter (skb, sk, snaplen);
    if (!res)
        goto drop_n_restore;
    if (snaplen > res)
        snaplen = res;
//
    if (atomic_read (&sk->sk_rmem_alloc) + skb->truesize >=    (unsigned) sk->sk_rcvbuf)
        goto drop_n_acct;

    if (skb_shared (skb))
    {
        struct sk_buff *nskb = skb_clone (skb, GFP_ATOMIC);
        if (nskb == NULL)
            goto drop_n_acct;

        if (skb_head != skb->data)
        {
            skb->data = skb_head;
            skb->len = skb_len;
        }
        kfree_skb (skb);
        skb = nskb;
    }

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

    sll = &PACKET_SKB_CB (skb)->sa.ll;
    sll->sll_family = AF_PACKET;
    sll->sll_hatype = dev->type;
    sll->sll_protocol = skb->protocol;
    sll->sll_pkttype = skb->pkt_type;
    if (unlikely (po->origdev))
        sll->sll_ifindex = orig_dev->ifindex;
    else
        sll->sll_ifindex = dev->ifindex;

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

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

    if (pskb_trim (skb, snaplen))
        goto drop_n_acct;

    skb_set_owner_r (skb, sk);
    skb->dev = NULL;
    dst_release (skb->dst);
    skb->dst = NULL;

    /* drop conntrack reference */
    nf_reset (skb);

    spin_lock (&sk->sk_receive_queue.lock);
    po->stats.tp_packets++;
    __skb_queue_tail (&sk->sk_receive_queue, skb);
    spin_unlock (&sk->sk_receive_queue.lock);
    sk->sk_data_ready (sk, skb->len);
    return 0;

drop_n_acct:
    spin_lock (&sk->sk_receive_queue.lock);
    po->stats.tp_drops++;
    spin_unlock (&sk->sk_receive_queue.lock);

drop_n_restore:
    if (skb_head != skb->data && skb_shared (skb))
    {
        skb->data = skb_head;
        skb->len = skb_len;
    }
drop:
    kfree_skb (skb);
    return 0;
}


static int packet_sendmsg (struct kiocb *iocb, struct socket *sock,
                              struct msghdr *msg, size_t len)
{
    struct sock *sk = sock->sk;
    struct sockaddr_ll *saddr = (struct sockaddr_ll *) msg->msg_name;
    struct sk_buff *skb;
    struct net_device *dev;
    __be16 proto;
    unsigned char *addr;
    int ifindex, err, reserve = 0;

    /*
     *  Get and verify the address.
     */

    if (saddr == NULL)
    {
        struct packet_sock *po = pkt_sk (sk);

        ifindex = po->ifindex;
        proto = po->num;
        addr = NULL;
    }
    else
    {
        err = -EINVAL;
        if (msg->msg_namelen < sizeof (struct sockaddr_ll))
            goto out;
        if (msg->msg_namelen < (saddr->sll_halen + offsetof (struct sockaddr_ll,
                    sll_addr)))
              goto out;
        ifindex = saddr->sll_ifindex;
        proto = saddr->sll_protocol;
        addr = saddr->sll_addr;
    }

    dev = dev_get_by_index (sock_net (sk), ifindex);
    err = -ENXIO;
    if (dev == NULL)
        goto out_unlock;
    if (sock->type == SOCK_RAW)
        reserve = dev->hard_header_len;

    err = -ENETDOWN;
    if (!(dev->flags & IFF_UP))
        goto out_unlock;

    err = -EMSGSIZE;
    if (len > dev->mtu + reserve)
        goto out_unlock;

    skb = sock_alloc_send_skb (sk, len + LL_ALLOCATED_SPACE (dev),
        msg->msg_flags & MSG_DONTWAIT, &err);
    if (skb == NULL)
        goto out_unlock;

    skb_reserve (skb, LL_RESERVED_SPACE (dev));
    skb_reset_network_header (skb);

    err = -EINVAL;
    if (sock->type == SOCK_DGRAM &&
        dev_hard_header (skb, dev, ntohs (proto), addr, NULL, len) < 0)
        goto out_free;

    /* Returns -EFAULT on error */
    err = memcpy_fromiovec (skb_put (skb, len), msg->msg_iov, len);
    if (err)
        goto out_free;

    skb->protocol = proto;
    skb->dev = dev;
    skb->priority = sk->sk_priority;

    /*
     *  Now send it
     */

    err = dev_queue_xmit (skb);
    if (err > 0 && (err = net_xmit_errno (err)) != 0)
        goto out_unlock;

    dev_put (dev);

    return (len);

  out_free:
    kfree_skb (skb);
  out_unlock:
    if (dev)
        dev_put (dev);
  out:
    return err;
}

/*
 *    Close a PACKET socket. This is fairly simple. We immediately go
 *    to 'closed' state and remove our protocol entry in the device list.
 */

static int packet_release (struct socket *sock)
{
    struct sock *sk = sock->sk;
    struct packet_sock *po;
    struct net *net;

    if (!sk)
        return 0;

    net = sock_net (sk);
    po = pkt_sk (sk);

    write_lock_bh (&packet.sklist_lock);
    sk_del_node_init (sk);
    write_unlock_bh (&packet.sklist_lock);

    /*
     *  Unhook packet receive handler.
     */

    if (po->running)
    {
        /*
         *  Remove the protocol hook
         */
        dev_remove_pack (&po->prot_hook);
        po->running = 0;
        po->num = 0;
        __sock_put (sk);
    }

    packet_flush_mclist (sk);

    /*
     *  Now the socket is dead. No more input will appear.
     */

    sock_orphan (sk);
    sock->sk = NULL;

    /* Purge queues */

    skb_queue_purge (&sk->sk_receive_queue);
    sk_refcnt_debug_release (sk);

    sock_put (sk);
    return 0;
}

/*
 *    Attach a packet hook.
 */

static int packet_do_bind (struct sock *sk, struct net_device *dev,
    __be16 protocol)
{
    struct packet_sock *po = pkt_sk (sk);
    /*
     *  Detach an existing hook if present.
     */

    lock_sock (sk);

    spin_lock (&po->bind_lock);
    if (po->running)
    {
        __sock_put (sk);
        po->running = 0;
        po->num = 0;
        spin_unlock (&po->bind_lock);
        dev_remove_pack (&po->prot_hook);
        spin_lock (&po->bind_lock);
    }

    po->num = protocol;
    po->prot_hook.type = protocol;
    po->prot_hook.dev = dev;

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

    if (protocol == 0)
        goto out_unlock;

    if (!dev || (dev->flags & IFF_UP))
    {
        dev_add_pack (&po->prot_hook);
        sock_hold (sk);
        po->running = 1;
    }
    else
    {
        sk->sk_err = ENETDOWN;
        if (!sock_flag (sk, SOCK_DEAD))
            sk->sk_error_report (sk);
    }

  out_unlock:
    spin_unlock (&po->bind_lock);
    release_sock (sk);
    return 0;
}

/*
 *    Bind a packet socket to a device
 */

static int packet_bind (struct socket *sock, struct sockaddr *uaddr,
    int addr_len)
{
    struct sockaddr_ll *sll = (struct sockaddr_ll *) uaddr;
    struct sock *sk = sock->sk;
    struct net_device *dev = NULL;
    int err;

    /*
     *  Check legality
     */

    if (addr_len < sizeof (struct sockaddr_ll))
        return -EINVAL;
    if (sll->sll_family != MPF_PACKET)
        return -EINVAL;

    if (sll->sll_ifindex)
    {
        err = -ENODEV;
        dev = dev_get_by_index (sock_net (sk), sll->sll_ifindex);
        if (dev == NULL)
            goto out;
    }
    err = packet_do_bind (sk, dev, sll->sll_protocol ? : pkt_sk (sk)->num);
    if (dev)
        dev_put (dev);

  out:
    return err;
}

static struct proto packet_proto = {
    .name = "MPACKET",
    .owner = THIS_MODULE,
    .obj_size = sizeof (struct packet_sock),
};

/*
 *    Create a packet of type _PACKET.
 */

static int packet_create (struct net *net, struct socket *sock, int protocol)
{
    struct sock *sk;
    struct packet_sock *po;
    __be16 proto = (__force __be16) protocol;    /* weird, but documented */
    int err;

    if (!capable (CAP_NET_RAW))
        return -EPERM;
    if (sock->type != SOCK_DGRAM && sock->type != SOCK_RAW )
        return -ESOCKTNOSUPPORT;

    sock->state = SS_UNCONNECTED;

    err = -ENOBUFS;
    sk = sk_alloc (net, MPF_PACKET, GFP_KERNEL, &packet_proto);
    if (sk == NULL)
        goto out;

    sock->ops = &packet_ops;  

    sock_init_data (sock, sk);

    po = pkt_sk (sk);
    sk->sk_family = MPF_PACKET;
    po->num = proto;

    sk->sk_destruct = packet_sock_destruct;
    sk_refcnt_debug_inc (sk);

    /*
     *  Attach a protocol block
     */

    spin_lock_init (&po->bind_lock);
    mutex_init (&po->pg_vec_lock);
    po->prot_hook.func = packet_rcv;

  
    po->prot_hook.af_packet_priv = sk;

    if (proto)
    {
        po->prot_hook.type = proto;
        dev_add_pack (&po->prot_hook);
        sock_hold (sk);
        po->running = 1;
    }

    write_lock_bh (&packet.sklist_lock);
    sk_add_node (sk, &packet.sklist);
    write_unlock_bh (&packet.sklist_lock);
    return (0);
  out:
    return err;
}

/*
 *    Pull a packet from our receive queue and hand it to the user.
 *    If necessary we block.
 */

static int packet_recvmsg (struct kiocb *iocb, struct socket *sock,
    struct msghdr *msg, size_t len, int flags)
{
    struct sock *sk = sock->sk;
    struct sk_buff *skb;
    int copied, err;
    struct sockaddr_ll *sll;

    err = -EINVAL;
    if (flags & ~(MSG_PEEK | MSG_DONTWAIT | MSG_TRUNC | MSG_CMSG_COMPAT))
        goto out;

#if 0
    /* What error should we return now? EUNATTACH? */
    if (pkt_sk (sk)->ifindex < 0)
        return -ENODEV;
#endif

    /*
     *  Call the generic datagram receiver. This handles all sorts
     *  of horrible races and re-entrancy so we can forget about it
     *  in the protocol layers.
     *
     *  Now it will return ENETDOWN, if device have just gone down,
     *  but then it will block.
     */

    skb = skb_recv_datagram (sk, flags, flags & MSG_DONTWAIT, &err);

    /*
     *  An error occurred so return it. Because skb_recv_datagram()
     *  handles the blocking we don't see and worry about blocking
     *  retries.
     */

    if (skb == NULL)
        goto out;

    /*
     *  If the address length field is there to be filled in, we fill
     *  it in now.
     */

    sll = &PACKET_SKB_CB (skb)->sa.ll;
  
    msg->msg_namelen = sll->sll_halen + offsetof (struct sockaddr_ll, sll_addr);

    /*
     *  You lose any data beyond the buffer you gave. If it worries a
     *  user program they can ask the device for its MTU anyway.
     */

    copied = skb->len;
    if (copied > len)
    {
        copied = len;
        msg->msg_flags |= MSG_TRUNC;
    }

    err = skb_copy_datagram_iovec (skb, 0, msg->msg_iov, copied);
    if (err)
        goto out_free;

    sock_recv_timestamp (msg, sk, skb);

    if (msg->msg_name)
        memcpy (msg->msg_name, &PACKET_SKB_CB (skb)->sa, msg->msg_namelen);

    if (pkt_sk (sk)->auxdata)
    {
        struct tpacket_auxdata aux;

        aux.tp_status = TP_STATUS_USER;
        if (skb->ip_summed == CHECKSUM_PARTIAL)
            aux.tp_status |= TP_STATUS_CSUMNOTREADY;
        aux.tp_len = PACKET_SKB_CB (skb)->origlen;
        aux.tp_snaplen = skb->len;
        aux.tp_mac = 0;
        aux.tp_net = skb_network_offset (skb);
        aux.tp_vlan_tci = skb->vlan_tci;

        put_cmsg (msg, SOL_PACKET, PACKET_AUXDATA, sizeof (aux), &aux);
    }

    /*
     *  Free or return the buffer as appropriate. Again this
     *  hides all the races and re-entrancy issues from us.
     */
    err = (flags & MSG_TRUNC) ? skb->len : copied;

  out_free:
    skb_free_datagram (sk, skb);
  out:
    return err;
}



static int packet_getname (struct socket *sock, struct sockaddr *uaddr,
    int *uaddr_len, int peer)
{
    struct net_device *dev;
    struct sock *sk = sock->sk;
    struct packet_sock *po = pkt_sk (sk);
    struct sockaddr_ll *sll = (struct sockaddr_ll *) uaddr;

    if (peer)
        return -EOPNOTSUPP;

    sll->sll_family = AF_PACKET;
    sll->sll_ifindex = po->ifindex;
    sll->sll_protocol = po->num;
    dev = dev_get_by_index (sock_net (sk), po->ifindex);
    if (dev)
    {
        sll->sll_hatype = dev->type;
        sll->sll_halen = dev->addr_len;
        memcpy (sll->sll_addr, dev->dev_addr, dev->addr_len);
        dev_put (dev);
    }
    else
    {
        sll->sll_hatype = 0;    /* Bad: we have no ARPHRD_UNSPEC */
        sll->sll_halen = 0;
    }
    *uaddr_len = offsetof (struct sockaddr_ll, sll_addr) + sll->sll_halen;

    return 0;
}

static int packet_dev_mc (struct net_device *dev, struct packet_mclist *i,
    int what)
{
    switch (i->type)
    {
    case PACKET_MR_MULTICAST:
        if (what > 0)
            dev_mc_add (dev, i->addr, i->alen, 0);
        else
            dev_mc_delete (dev, i->addr, i->alen, 0);
        break;
    case PACKET_MR_PROMISC:
        return dev_set_promiscuity (dev, what);
        break;
    case PACKET_MR_ALLMULTI:
        return dev_set_allmulti (dev, what);
        break;
    default:;
    }
    return 0;
}

static void packet_dev_mclist (struct net_device *dev, struct packet_mclist *i,
    int what)
{
    for (; i; i = i->next)
    {
        if (i->ifindex == dev->ifindex)
            packet_dev_mc (dev, i, what);
    }
}

static int packet_mc_add (struct sock *sk, struct packet_mreq_max *mreq)
{
    struct packet_sock *po = pkt_sk (sk);
    struct packet_mclist *ml, *i;
    struct net_device *dev;
    int err;

    rtnl_lock ();

    err = -ENODEV;
    dev = __dev_get_by_index (sock_net (sk), mreq->mr_ifindex);
    if (!dev)
        goto done;

    err = -EINVAL;
    if (mreq->mr_alen > dev->addr_len)
        goto done;

    err = -ENOBUFS;
    i = kmalloc (sizeof (*i), GFP_KERNEL);
    if (i == NULL)
        goto done;

    err = 0;
    for (ml = po->mclist; ml; ml = ml->next)
    {
        if (ml->ifindex == mreq->mr_ifindex &&
            ml->type == mreq->mr_type &&
            ml->alen == mreq->mr_alen &&
            memcmp (ml->addr, mreq->mr_address, ml->alen) == 0)
        {
            ml->count++;
            /* Free the new element ... */
            kfree (i);
            goto done;
        }
    }

    i->type = mreq->mr_type;
    i->ifindex = mreq->mr_ifindex;
    i->alen = mreq->mr_alen;
    memcpy (i->addr, mreq->mr_address, i->alen);
    i->count = 1;
    i->next = po->mclist;
    po->mclist = i;
    err = packet_dev_mc (dev, i, 1);
    if (err)
    {
        po->mclist = i->next;
        kfree (i);
    }

  done:
    rtnl_unlock ();
    return err;
}

static int packet_mc_drop (struct sock *sk, struct packet_mreq_max *mreq)
{
    struct packet_mclist *ml, **mlp;

    rtnl_lock ();

    for (mlp = &pkt_sk (sk)->mclist; (ml = *mlp) != NULL; mlp = &ml->next)
    {
        if (ml->ifindex == mreq->mr_ifindex &&
            ml->type == mreq->mr_type &&
            ml->alen == mreq->mr_alen &&
            memcmp (ml->addr, mreq->mr_address, ml->alen) == 0)
        {
            if (--ml->count == 0)
            {
                struct net_device *dev;
                *mlp = ml->next;
                dev = dev_get_by_index (sock_net (sk), ml->ifindex);
                if (dev)
                {
                    packet_dev_mc (dev, ml, -1);
                    dev_put (dev);
                }
                kfree (ml);
            }
            rtnl_unlock ();
            return 0;
        }
    }
    rtnl_unlock ();
    return -EADDRNOTAVAIL;
}

static void packet_flush_mclist (struct sock *sk)
{
    struct packet_sock *po = pkt_sk (sk);
    struct packet_mclist *ml;

    if (!po->mclist)
        return;

    rtnl_lock ();
    while ((ml = po->mclist) != NULL)
    {
        struct net_device *dev;

        po->mclist = ml->next;
        if ((dev = dev_get_by_index (sock_net (sk), ml->ifindex)) != NULL)
        {
            packet_dev_mc (dev, ml, -1);
            dev_put (dev);
        }
        kfree (ml);
    }
    rtnl_unlock ();
}

static int
packet_setsockopt (struct socket *sock, int level, int optname,
    char __user * optval, int optlen)
{
    struct sock *sk = sock->sk;
    struct packet_sock *po = pkt_sk (sk);
    int ret;

    if (level != SOL_PACKET)
        return -ENOPROTOOPT;

    switch (optname)
    {
    case PACKET_ADD_MEMBERSHIP:
    case PACKET_DROP_MEMBERSHIP:
        {
            struct packet_mreq_max mreq;
            int len = optlen;
            memset (&mreq, 0, sizeof (mreq));
            if (len < sizeof (struct packet_mreq))
                return -EINVAL;
            if (len > sizeof (mreq))
                len = sizeof (mreq);
            if (copy_from_user (&mreq, optval, len))
                return -EFAULT;
            if (len < (mreq.mr_alen + offsetof (struct packet_mreq,
                        mr_address)))
                  return -EINVAL;
            if (optname == PACKET_ADD_MEMBERSHIP)
                ret = packet_mc_add (sk, &mreq);
            else
                ret = packet_mc_drop (sk, &mreq);
            return ret;
        }
    case PACKET_AUXDATA:
        {
            int val;

            if (optlen < sizeof (val))
                return -EINVAL;
            if (copy_from_user (&val, optval, sizeof (val)))
                return -EFAULT;

            po->auxdata = !!val;
            return 0;
        }
    case PACKET_ORIGDEV:
        {
            int val;

            if (optlen < sizeof (val))
                return -EINVAL;
            if (copy_from_user (&val, optval, sizeof (val)))
                return -EFAULT;

            po->origdev = !!val;
            return 0;
        }
    default:
        return -ENOPROTOOPT;
    }
}

static int packet_getsockopt (struct socket *sock, int level, int optname,
    char __user * optval, int __user * optlen)
{
    int len;
    int val;
    struct sock *sk = sock->sk;
    struct packet_sock *po = pkt_sk (sk);
    void *data;
    struct tpacket_stats st;

    if (level != SOL_PACKET)
        return -ENOPROTOOPT;

    if (get_user (len, optlen))
        return -EFAULT;

    if (len < 0)
        return -EINVAL;

    switch (optname)
    {
    case PACKET_STATISTICS:
        if (len > sizeof (struct tpacket_stats))
            len = sizeof (struct tpacket_stats);
        spin_lock_bh (&sk->sk_receive_queue.lock);
        st = po->stats;
        memset (&po->stats, 0, sizeof (st));
        spin_unlock_bh (&sk->sk_receive_queue.lock);
        st.tp_packets += st.tp_drops;

        data = &st;
        break;
    case PACKET_AUXDATA:
        if (len > sizeof (int))
            len = sizeof (int);
        val = po->auxdata;

        data = &val;
        break;
    case PACKET_ORIGDEV:
        if (len > sizeof (int))
            len = sizeof (int);
        val = po->origdev;

        data = &val;
        break;

    default:
        return -ENOPROTOOPT;
    }

    if (put_user (len, optlen))
        return -EFAULT;
    if (copy_to_user (optval, data, len))
        return -EFAULT;
    return 0;
}

static int packet_notifier (struct notifier_block *this, unsigned long msg,
    void *data)
{
    struct sock *sk;
    struct hlist_node *node;
    struct net_device *dev = data;
   

    read_lock (&packet.sklist_lock);
    sk_for_each (sk, node, &packet.sklist)
    {
        struct packet_sock *po = pkt_sk (sk);

        switch (msg)
        {
        case NETDEV_UNREGISTER:
            if (po->mclist)
                packet_dev_mclist (dev, po->mclist, -1);
            /* fallthrough */

        case NETDEV_DOWN:
            if (dev->ifindex == po->ifindex)
            {
                spin_lock (&po->bind_lock);
                if (po->running)
                {
                    __dev_remove_pack (&po->prot_hook);
                    __sock_put (sk);
                    po->running = 0;
                    sk->sk_err = ENETDOWN;
                    if (!sock_flag (sk, SOCK_DEAD))
                        sk->sk_error_report (sk);
                }
                if (msg == NETDEV_UNREGISTER)
                {
                    po->ifindex = -1;
                    po->prot_hook.dev = NULL;
                }
                spin_unlock (&po->bind_lock);
            }
            break;
        case NETDEV_UP:
            spin_lock (&po->bind_lock);
            if (dev->ifindex == po->ifindex && po->num && !po->running)
            {
                dev_add_pack (&po->prot_hook);
                sock_hold (sk);
                po->running = 1;
            }
            spin_unlock (&po->bind_lock);
            break;
        }
    }
    read_unlock (&packet.sklist_lock);
    return NOTIFY_DONE;
}

static int packet_ioctl (struct socket *sock, unsigned int cmd,
                           unsigned long arg)
{
    struct sock *sk = sock->sk;

    switch (cmd)
    {
    case SIOCOUTQ:
        {
            int amount = atomic_read (&sk->sk_wmem_alloc);
            return put_user (amount, (int __user *) arg);
        }
    case SIOCINQ:
        {
            struct sk_buff *skb;
            int amount = 0;

            spin_lock_bh (&sk->sk_receive_queue.lock);
            skb = skb_peek (&sk->sk_receive_queue);
            if (skb)
                amount = skb->len;
            spin_unlock_bh (&sk->sk_receive_queue.lock);
            return put_user (amount, (int __user *) arg);
        }
    case SIOCGSTAMP:
        return sock_get_timestamp (sk, (struct timeval __user *) arg);
    case SIOCGSTAMPNS:
        return sock_get_timestampns (sk, (struct timespec __user *) arg);


    case SIOCADDRT:
    case SIOCDELRT:
    case SIOCDARP:
    case SIOCGARP:
    case SIOCSARP:
    case SIOCGIFADDR:
    case SIOCSIFADDR:
    case SIOCGIFBRDADDR:
    case SIOCSIFBRDADDR:
    case SIOCGIFNETMASK:
    case SIOCSIFNETMASK:
    case SIOCGIFDSTADDR:
    case SIOCSIFDSTADDR:
    case SIOCSIFFLAGS:
        if (!net_eq (sock_net (sk), &init_net))
            return -ENOIOCTLCMD;
        return inet_dgram_ops.ioctl (sock, cmd, arg);


    default:
        return -ENOIOCTLCMD;
    }
    return 0;
}

//
static const struct proto_ops packet_ops = {
    .family = MPF_PACKET,
    .owner = THIS_MODULE,
    .release = packet_release,
    .bind = packet_bind,
    .connect = sock_no_connect,
    .socketpair = sock_no_socketpair,
    .accept = sock_no_accept,
    .getname = packet_getname,
    .poll = datagram_poll,
    .ioctl = packet_ioctl,
    .listen = sock_no_listen,
    .shutdown = sock_no_shutdown,
    .setsockopt = packet_setsockopt,
    .getsockopt = packet_getsockopt,
    .sendmsg = packet_sendmsg,
    .recvmsg = packet_recvmsg,
    .mmap = sock_no_mmap,
    .sendpage = sock_no_sendpage,
};

static struct net_proto_family packet_family_ops = {
    .family = MPF_PACKET,
    .create = packet_create,
    .owner = THIS_MODULE,
};

static struct notifier_block packet_netdev_notifier = {
    .notifier_call = packet_notifier,
};





//SYS
static inline struct sock *packet_seq_idx ( loff_t off)
{
    struct sock *s;
    struct hlist_node *node;

    sk_for_each (s, node, &packet.sklist)
    {
        if (!off--)
            return s;
    }
    return NULL;
}

static void *packet_seq_start (struct seq_file *seq, loff_t * pos)
{
  
    read_lock (&packet.sklist_lock);
    return *pos ? packet_seq_idx (*pos - 1) : SEQ_START_TOKEN;
}

static void *packet_seq_next (struct seq_file *seq, void *v, loff_t * pos)
{
    ++*pos;
    return (v == SEQ_START_TOKEN)
        ? sk_head (&packet.sklist) : sk_next ((struct sock *) v);
}

static void packet_seq_stop (struct seq_file *seq, void *v)
__releases (seq_file_net (seq)->packet.sklist_lock)
{
    read_unlock (&packet.sklist_lock);
}

static int packet_seq_show (struct seq_file *seq, void *v)
{
    if (v == SEQ_START_TOKEN)
        seq_puts (seq,
            "sk       RefCnt Type Proto  Iface R Rmem   User   Inode\n");
    else
    {
        struct sock *s = v;
        const struct packet_sock *po = pkt_sk (s);

        seq_printf (seq,
            "%p %-6d %-4d %04x   %-5d %1d %-6u %-6u %-6lu\n",
            s,
            atomic_read (&s->sk_refcnt),
            s->sk_type,
            ntohs (po->num),
            po->ifindex,
            po->running,
            atomic_read (&s->sk_rmem_alloc), sock_i_uid (s), sock_i_ino (s));
    }

    return 0;
}

static const struct seq_operations packet_seq_ops = {
    .start = packet_seq_start,
    .next = packet_seq_next,
    .stop = packet_seq_stop,
    .show = packet_seq_show,
};

static int packet_seq_open (struct inode *inode, struct file *file)
{
    return seq_open_net (inode, file, &packet_seq_ops,
        sizeof (struct seq_net_private));
}

static const struct file_operations packet_seq_fops = {
    .owner = THIS_MODULE,
    .open = packet_seq_open,
    .read = seq_read,
    .llseek = seq_lseek,
    .release = seq_release_net,
};



static int packet_net_init (struct net *net)
{
    rwlock_init (&packet.sklist_lock);
    INIT_HLIST_HEAD (&packet.sklist);

    if (!proc_net_fops_create (net, "mpacket", 0, &packet_seq_fops))
        return -ENOMEM;

    return 0;
}

static void packet_net_exit (struct net *net)
{
    proc_net_remove (net, "mpacket");
}

static struct pernet_operations packet_net_ops = {
    .init = packet_net_init,
    .exit = packet_net_exit,
};

static void __exit packet_exit (void)
{
    unregister_netdevice_notifier (&packet_netdev_notifier);
    unregister_pernet_subsys (&packet_net_ops);
    sock_unregister (MPF_PACKET);
    proto_unregister (&packet_proto);
}

static int __init packet_init (void)
{
    int rc = proto_register (&packet_proto, 0);

    if (rc != 0)
        goto out;

    sock_register (&packet_family_ops);
    register_pernet_subsys (&packet_net_ops);
    register_netdevice_notifier (&packet_netdev_notifier);
  out:
    return rc;
}

module_init (packet_init);
module_exit (packet_exit);
MODULE_LICENSE ("GPL");
MODULE_ALIAS_NETPROTO (MPF_PACKET);





















////////////////////////////
/***************************
测试代码
***************************/

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include         /* the L2 protocols */

#define BUFFER_MAX 2048
#define MPF_PACKET AF_ROSE

int bind_eth (int fd)
{
    struct sockaddr_ll sll;
    struct ifreq ifstruct;
    struct packet_mreq mr;
    int ret;

    memset (&sll, 0, sizeof (sll));
    sll.sll_family = MPF_PACKET;
    sll.sll_protocol = htons (ETH_P_ALL);

    strcpy (ifstruct.ifr_name, "eth1");
    ret = ioctl (fd, SIOCGIFINDEX, &ifstruct);
    if (ret != 0)
    {
        printf ("line:% d\n", __LINE__);
    }

    sll.sll_ifindex = ifstruct.ifr_ifindex;
    mr.mr_ifindex = ifstruct.ifr_ifindex;

    ret = ioctl (fd, SIOCGIFHWADDR, &ifstruct);
    if (ret != 0)
    {
        printf ("line:% d\n", __LINE__);
    }
    memcpy (sll.sll_addr, ifstruct.ifr_ifru.ifru_hwaddr.sa_data,
        sizeof (sll.sll_addr));

    if (bind (fd, (struct sockaddr *) &sll, sizeof (sll)) == -1)
    {
        printf ("bind: ERROR\n");
        return -1;
    }
 

    return 0;
}

char tbuffer[BUFFER_MAX]=
{
 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
 0x12,0x34,0x56,0x78,0x9a,0xbc,0xde,
};

int main (int argc, char *argv[])
{

    int sock, n_read, proto;
    char buffer[BUFFER_MAX];
    char *ethhead, *iphead, *tcphead, *udphead, *icmphead, *p;

    if ((sock = socket (MPF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 0)
    {
        fprintf (stdout, "create socket error\n");
        return -1;
    }

    bind_eth (sock);
   
    sendto(sock, tbuffer, 18, 0, 0, 0);

    while (1)
    {
        n_read = recvfrom (sock, buffer, 2048, 0, NULL, NULL);
    /*
    14 6(dest)+6(source)+2(type or length)
    +
    20 ip header
    +
    8 icmp,tcp or udp header
    = 42
    */
        if (n_read < 42)
        {
            fprintf (stdout, "Incomplete header, packet corrupt\n");
            continue;
        }

        ethhead = buffer;
        p = ethhead;
        int n = 0xFF;
        printf ("MAC: %.2X:%02X:%02X:%02X:%02X:%02X==>"
            "%.2X:%.2X:%.2X:%.2X:%.2X:%.2X\n",
            p[6] & n, p[7] & n, p[8] & n, p[9] & n, p[10] & n, p[11] & n,
            p[0] & n, p[1] & n, p[2] & n, p[3] & n, p[4] & n, p[5] & n);

        iphead = ethhead + 14;
        p = iphead + 12;
       
        if ( *(unsigned short*)(ethhead+12) != 0x0800 )
        {
             printf("proto: 0x%x\n",*(unsigned short*)(ethhead+12));
             continue;
        }

        printf ("IP: %d.%d.%d.%d => %d.%d.%d.%d\n",
            p[0] & 0XFF, p[1] & 0XFF, p[2] & 0XFF, p[3] & 0XFF,
            p[4] & 0XFF, p[5] & 0XFF, p[6] & 0XFF, p[7] & 0XFF);
        proto = (iphead + 9)[0];
        p = iphead + 20;
        printf ("Protocol: ");
        switch (proto)
        {
        case IPPROTO_ICMP:
            printf ("ICMP\n");
            break;
        case IPPROTO_IGMP:
            printf ("IGMP\n");
            break;
        case IPPROTO_IPIP:
            printf ("IPIP\n");
            break;
        case IPPROTO_TCP:
        case IPPROTO_UDP:
            printf ("%s,", proto == IPPROTO_TCP ? "TCP" : "UDP");
            printf ("source port: %u,", (p[0] << 8) & 0XFF00 | p[1] & 0XFF);
            printf ("dest port: %u\n", (p[2] << 8) & 0XFF00 | p[3] & 0XFF);
            break;
        case IPPROTO_RAW:
            printf ("RAW\n");
            break;
        default:
            printf ("Unkown, please query in include/linux/in.h\n");
        }
    }
}










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

上一篇:Linux 内核发包

下一篇:绑定网口

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