This is AF_PACKET mmap compat support.

I confirmed that 32-bit and 64-bit tcpdump binaries using AF_PACKET
mmap'ed pcap can dump packets properly with a 64-bit kernel.

Thanks to David for all your help.

Signed-off-by: FUJITA Tomonori <[EMAIL PROTECTED]>
---

 net/packet/af_packet.c |  156 +++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 128 insertions(+), 28 deletions(-)

a4fd7acdecf12871d91976610cd3d1442807b206
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 9db7dbd..71ab97a 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -78,6 +78,7 @@
 #include <linux/poll.h>
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/compat.h>
 
 #ifdef CONFIG_INET
 #include <net/inet_common.h>
@@ -182,6 +183,31 @@ struct packet_mreq_max
 #endif
 #ifdef CONFIG_PACKET_MMAP
 static int packet_set_ring(struct sock *sk, struct tpacket_req *req, int 
closing);
+
+#ifdef CONFIG_COMPAT
+struct tpacket_hdr_compat {
+       compat_ulong_t  tp_status;
+       compat_uint_t   tp_len;
+       compat_uint_t   tp_snaplen;
+       unsigned short  tp_mac;
+       unsigned short  tp_net;
+       compat_uint_t   tp_sec;
+       compat_uint_t   tp_usec;
+};
+
+#ifdef CONFIG_X86_64
+#  define COMPAT_TEST is_compat_task()
+#elif defined(CONFIG_IA64)
+#  define COMPAT_TEST IS_IA32_PROCESS(task_pt_regs(current))
+#elif defined(CONFIG_S390)
+#  define COMPAT_TEST test_thread_flag(TIF_31BIT)
+#elif defined(CONFIG_MIPS)
+#  define COMPAT_TEST (current->thread.mflags & MF_32BIT_ADDR)
+#else
+#  define COMPAT_TEST test_thread_flag(TIF_32BIT)
+#endif
+#endif
+
 #endif
 
 static void packet_flush_mclist(struct sock *sk);
@@ -211,6 +237,9 @@ struct packet_sock {
        unsigned int            pg_vec_order;
        unsigned int            pg_vec_pages;
        unsigned int            pg_vec_len;
+#ifdef CONFIG_COMPAT
+       int                     compat;
+#endif
 #endif
 };
 
@@ -562,15 +591,93 @@ drop:
 }
 
 #ifdef CONFIG_PACKET_MMAP
+#define        __tpacket_hdr_update(h, skb, snaplen, mac, net, status)         
\
+do {                                                                   \
+               h->tp_len = (skb)->len;                                 \
+               h->tp_snaplen = snaplen;                                \
+               h->tp_mac = mac;                                        \
+               h->tp_net = net;                                        \
+               h->tp_sec = (skb)->tstamp.off_sec;                      \
+               h->tp_usec = (skb)->tstamp.off_usec;                    \
+               h->tp_status = status;                                  \
+} while (0)
+
+#ifdef CONFIG_COMPAT
+static void tpacket_hdr_update(struct packet_sock *po, unsigned int idx,
+                              struct sk_buff *skb,
+                              unsigned int snaplen, unsigned short mac,
+                              unsigned short net, unsigned long status)
+{
+       if (po->compat) {
+               struct tpacket_hdr_compat *h;
+               h = (struct tpacket_hdr_compat *)
+                       packet_lookup_frame(po, idx);
+               __tpacket_hdr_update(h, skb, snaplen, mac, net, status);
+       } else {
+               struct tpacket_hdr *h;
+               h = (struct tpacket_hdr *) packet_lookup_frame(po, idx);
+               __tpacket_hdr_update(h, skb, snaplen, mac, net, status);
+       }
+       mb();
+}
+
+static unsigned int tpacket_hdr_status(struct packet_sock *po, unsigned int 
idx)
+{
+       if (po->compat) {
+               struct tpacket_hdr_compat *h;
+               h = (struct tpacket_hdr_compat *)
+                       packet_lookup_frame(po, idx);
+               return h->tp_status;
+       } else {
+               struct tpacket_hdr *h;
+               h = (struct tpacket_hdr *) packet_lookup_frame(po, idx);
+               return h->tp_status;
+       }
+}
+
+static size_t tpacket_hdr_size(struct packet_sock *po)
+{
+       return po->compat ? sizeof(struct tpacket_hdr_compat) :
+               sizeof(struct tpacket_hdr);
+}
+#else
+
+static void tpacket_hdr_update(struct packet_sock *po, unsigned int idx,
+                              struct sk_buff *skb,
+                              unsigned int snaplen, unsigned short mac,
+                              unsigned short net, unsigned long status)
+{
+       struct tpacket_hdr *h;
+       h = (struct tpacket_hdr *) packet_lookup_frame(po, idx);
+       __tpacket_hdr_update(h, skb, snaplen, mac, net, status);
+}
+
+static unsigned long tpacket_hdr_status(struct packet_sock *po, unsigned int 
idx)
+{
+       struct tpacket_hdr *h;
+       h = (struct tpacket_hdr *) packet_lookup_frame(po, idx);
+       return h->tp_status;
+}
+
+static size_t tpacket_hdr_size(struct packet_sock *po)
+{
+       return sizeof(struct tpacket_hdr);
+}
+#endif
+
+static int tpacket_hdrlen(struct packet_sock *po)
+{
+       return TPACKET_ALIGN(tpacket_hdr_size(po)) + sizeof(struct sockaddr_ll);
+}
+
 static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct 
packet_type *pt, struct net_device *orig_dev)
 {
        struct sock *sk;
        struct packet_sock *po;
        struct sockaddr_ll *sll;
-       struct tpacket_hdr *h;
-       u8 * skb_head = skb->data;
+       u8 * skb_head = skb->data, *cur_frame;
        int skb_len = skb->len;
-       unsigned snaplen;
+       unsigned snaplen, idx;
        unsigned long status = TP_STATUS_LOSING|TP_STATUS_USER;
        unsigned short macoff, netoff;
        struct sk_buff *copy_skb = NULL;
@@ -603,10 +710,10 @@ static int tpacket_rcv(struct sk_buff *s
        }
 
        if (sk->sk_type == SOCK_DGRAM) {
-               macoff = netoff = TPACKET_ALIGN(TPACKET_HDRLEN) + 16;
+               macoff = netoff = TPACKET_ALIGN(tpacket_hdrlen(po)) + 16;
        } else {
                unsigned maclen = skb->nh.raw - skb->data;
-               netoff = TPACKET_ALIGN(TPACKET_HDRLEN + (maclen < 16 ? 16 : 
maclen));
+               netoff = TPACKET_ALIGN(tpacket_hdrlen(po) + (maclen < 16 ? 16 : 
maclen));
                macoff = netoff - maclen;
        }
 
@@ -631,9 +738,9 @@ static int tpacket_rcv(struct sk_buff *s
                snaplen = skb->len-skb->data_len;
 
        spin_lock(&sk->sk_receive_queue.lock);
-       h = (struct tpacket_hdr *)packet_lookup_frame(po, po->head);
-       
-       if (h->tp_status)
+       idx = po->head;
+       cur_frame = packet_lookup_frame(po, idx);
+       if (tpacket_hdr_status(po, idx))
                goto ring_is_full;
        po->head = po->head != po->frame_max ? po->head+1 : 0;
        po->stats.tp_packets++;
@@ -645,20 +752,14 @@ static int tpacket_rcv(struct sk_buff *s
                status &= ~TP_STATUS_LOSING;
        spin_unlock(&sk->sk_receive_queue.lock);
 
-       memcpy((u8*)h + macoff, skb->data, snaplen);
+       memcpy(cur_frame + macoff, skb->data, snaplen);
 
-       h->tp_len = skb->len;
-       h->tp_snaplen = snaplen;
-       h->tp_mac = macoff;
-       h->tp_net = netoff;
-       if (skb->tstamp.off_sec == 0) { 
+       if (skb->tstamp.off_sec == 0) {
                __net_timestamp(skb);
                sock_enable_timestamp(sk);
        }
-       h->tp_sec = skb->tstamp.off_sec;
-       h->tp_usec = skb->tstamp.off_usec;
 
-       sll = (struct sockaddr_ll*)((u8*)h + TPACKET_ALIGN(sizeof(*h)));
+       sll = (struct sockaddr_ll*)(cur_frame + 
TPACKET_ALIGN(tpacket_hdr_size(po)));
        sll->sll_halen = 0;
        if (dev->hard_header_parse)
                sll->sll_halen = dev->hard_header_parse(skb, sll->sll_addr);
@@ -668,14 +769,12 @@ static int tpacket_rcv(struct sk_buff *s
        sll->sll_pkttype = skb->pkt_type;
        sll->sll_ifindex = dev->ifindex;
 
-       h->tp_status = status;
-       mb();
-
+       tpacket_hdr_update(po, idx, skb, snaplen, macoff, netoff, status);
        {
                struct page *p_start, *p_end;
-               u8 *h_end = (u8 *)h + macoff + snaplen - 1;
+               u8 *h_end = cur_frame + macoff + snaplen - 1;
 
-               p_start = virt_to_page(h);
+               p_start = virt_to_page(cur_frame);
                p_end = virt_to_page(h_end);
                while (p_start <= p_end) {
                        flush_dcache_page(p_start);
@@ -1542,11 +1641,8 @@ static unsigned int packet_poll(struct f
        spin_lock_bh(&sk->sk_receive_queue.lock);
        if (po->pg_vec) {
                unsigned last = po->head ? po->head-1 : po->frame_max;
-               struct tpacket_hdr *h;
-
-               h = (struct tpacket_hdr *)packet_lookup_frame(po, last);
 
-               if (h->tp_status)
+               if (tpacket_hdr_status(po, last))
                        mask |= POLLIN | POLLRDNORM;
        }
        spin_unlock_bh(&sk->sk_receive_queue.lock);
@@ -1636,7 +1732,11 @@ static int packet_set_ring(struct sock *
        struct packet_sock *po = pkt_sk(sk);
        int was_running, num, order = 0;
        int err = 0;
-       
+
+#ifdef CONFIG_COMPAT
+       po->compat = COMPAT_TEST;
+#endif
+
        if (req->tp_block_nr) {
                int i, l;
 
@@ -1649,7 +1749,7 @@ static int packet_set_ring(struct sock *
                        return -EINVAL;
                if (unlikely(req->tp_block_size & (PAGE_SIZE - 1)))
                        return -EINVAL;
-               if (unlikely(req->tp_frame_size < TPACKET_HDRLEN))
+               if (unlikely(req->tp_frame_size < tpacket_hdrlen(po)))
                        return -EINVAL;
                if (unlikely(req->tp_frame_size & (TPACKET_ALIGNMENT - 1)))
                        return -EINVAL;
-- 
1.1.3
-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to