summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'tags/2.6.20-5/20962_linux-2.6-xen-add-packet_auxdata-cmsg-1.patch')
-rw-r--r--tags/2.6.20-5/20962_linux-2.6-xen-add-packet_auxdata-cmsg-1.patch233
1 files changed, 233 insertions, 0 deletions
diff --git a/tags/2.6.20-5/20962_linux-2.6-xen-add-packet_auxdata-cmsg-1.patch b/tags/2.6.20-5/20962_linux-2.6-xen-add-packet_auxdata-cmsg-1.patch
new file mode 100644
index 0000000..16f11fa
--- /dev/null
+++ b/tags/2.6.20-5/20962_linux-2.6-xen-add-packet_auxdata-cmsg-1.patch
@@ -0,0 +1,233 @@
+From: Herbert Xu <herbert.xu@redhat.com>
+Subject: [RHEL5 PATCH] [PACKET]: Add PACKET_AUXDATA cmsg
+Date: Wed, 10 Jan 2007 20:17:36 +1100
+Message-Id: <20070110091736.GA28579@gondor.apana.org.au>
+Changelog: xen: Add PACKET_AUXDATA cmsg
+
+
+Hi:
+
+RHEL5 BZ 219681
+
+This patch forms part of the solution to #219681 where the DHCP server
+can't serve any requests from clients running (in different domains)
+on the same Xen host.
+
+I've sent a similar patch (without the Xen-specific code) upstream.
+
+This obsoletes the previous patch that computed the checksums in
+kernel-space.
+
+[PACKET]: Add PACKET_AUXDATA cmsg
+
+HCP servers/clients using AF_PACKET) to be able to serve another
+client on the same Xen host.
+
+The problem is that packets between different domains on the same
+Xen host only have partial checksums. Unfortunately this piece of
+information is not passed along in AF_PACKET unless you're using
+the mmap interface. Since dhcpd doesn't support packet-mmap, UDP
+packets from the same host come out with apparently bogus checksums.
+
+This patch adds a mechanism for AF_PACKET recvmsg(2) to return the
+status along with the packet. It does so by adding a new cmsg that
+contains this information along with some other relevant data such
+as the original packet length.
+
+I didn't include the time stamp information since there is already
+a cmsg for that.
+
+This patch also changes the mmap code to set the CSUMNOTREADY flag
+on all packets instead of just outoing packets on cooked sockets.
+
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+
+Index: linux-2.6.20.i386/include/linux/if_packet.h
+===================================================================
+--- linux-2.6.20.i386.orig/include/linux/if_packet.h
++++ linux-2.6.20.i386/include/linux/if_packet.h
+@@ -41,6 +41,7 @@ struct sockaddr_ll
+ #define PACKET_RX_RING 5
+ #define PACKET_STATISTICS 6
+ #define PACKET_COPY_THRESH 7
++#define PACKET_AUXDATA 8
+
+ struct tpacket_stats
+ {
+@@ -48,6 +49,15 @@ struct tpacket_stats
+ unsigned int tp_drops;
+ };
+
++struct tpacket_auxdata
++{
++ __u32 tp_status;
++ __u32 tp_len;
++ __u32 tp_snaplen;
++ __u16 tp_mac;
++ __u16 tp_net;
++};
++
+ struct tpacket_hdr
+ {
+ unsigned long tp_status;
+Index: linux-2.6.20.i386/net/packet/af_packet.c
+===================================================================
+--- linux-2.6.20.i386.orig/net/packet/af_packet.c
++++ linux-2.6.20.i386/net/packet/af_packet.c
+@@ -200,7 +200,8 @@ struct packet_sock {
+ #endif
+ struct packet_type prot_hook;
+ spinlock_t bind_lock;
+- char running; /* prot_hook is attached*/
++ unsigned int running:1, /* prot_hook is attached*/
++ auxdata:1;
+ int ifindex; /* bound device */
+ __be16 num;
+ #ifdef CONFIG_PACKET_MULTICAST
+@@ -214,6 +215,10 @@ struct packet_sock {
+ #endif
+ };
+
++#define PACKET_SKB_CB(__skb) ((struct tpacket_auxdata *)((__skb)->cb))
++
++extern int skb_checksum_setup(struct sk_buff *skb);
++
+ #ifdef CONFIG_PACKET_MMAP
+
+ static inline char *packet_lookup_frame(struct packet_sock *po, unsigned int position)
+@@ -462,6 +467,7 @@ static int packet_rcv(struct sk_buff *sk
+ u8 * skb_head = skb->data;
+ int skb_len = skb->len;
+ unsigned int snaplen, res;
++ struct tpacket_auxdata *aux;
+
+ if (skb->pkt_type == PACKET_LOOPBACK)
+ goto drop;
+@@ -523,6 +529,18 @@ static int packet_rcv(struct sk_buff *sk
+ if (dev->hard_header_parse)
+ sll->sll_halen = dev->hard_header_parse(skb, sll->sll_addr);
+
++ if (skb_checksum_setup(skb))
++ goto drop_n_acct;
++
++ aux = PACKET_SKB_CB(skb);
++ aux->tp_status = TP_STATUS_USER;
++ if (skb->ip_summed == CHECKSUM_PARTIAL)
++ aux->tp_status |= TP_STATUS_CSUMNOTREADY;
++ aux->tp_len = skb->len;
++ aux->tp_snaplen = snaplen;
++ aux->tp_mac = 0;
++ aux->tp_net = skb->nh.raw - skb->data;
++
+ if (pskb_trim(skb, snaplen))
+ goto drop_n_acct;
+
+@@ -582,8 +600,6 @@ static int tpacket_rcv(struct sk_buff *s
+ else if (skb->pkt_type == PACKET_OUTGOING) {
+ /* Special case: outgoing packets have ll header at head */
+ skb_pull(skb, skb->nh.raw - skb->data);
+- if (skb->ip_summed == CHECKSUM_PARTIAL)
+- status |= TP_STATUS_CSUMNOTREADY;
+ }
+ }
+
+@@ -595,6 +611,11 @@ static int tpacket_rcv(struct sk_buff *s
+ if (snaplen > res)
+ snaplen = res;
+
++ if (skb_checksum_setup(skb))
++ goto drop;
++ if (skb->ip_summed == CHECKSUM_PARTIAL)
++ status |= TP_STATUS_CSUMNOTREADY;
++
+ if (sk->sk_type == SOCK_DGRAM) {
+ macoff = netoff = TPACKET_ALIGN(TPACKET_HDRLEN) + 16;
+ } else {
+@@ -1119,6 +1140,11 @@ static int packet_recvmsg(struct kiocb *
+ if (msg->msg_name)
+ memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
+
++ if (pkt_sk(sk)->auxdata) {
++ struct tpacket_auxdata *aux = PACKET_SKB_CB(skb);
++ 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.
+@@ -1317,6 +1343,7 @@ 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)
+@@ -1369,6 +1396,18 @@ packet_setsockopt(struct socket *sock, i
+ return 0;
+ }
+ #endif
++ 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;
++ }
+ default:
+ return -ENOPROTOOPT;
+ }
+@@ -1378,8 +1417,11 @@ static int packet_getsockopt(struct sock
+ 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;
+@@ -1392,9 +1434,6 @@ static int packet_getsockopt(struct sock
+
+ switch(optname) {
+ case PACKET_STATISTICS:
+- {
+- struct tpacket_stats st;
+-
+ if (len > sizeof(struct tpacket_stats))
+ len = sizeof(struct tpacket_stats);
+ spin_lock_bh(&sk->sk_receive_queue.lock);
+@@ -1403,16 +1442,23 @@ static int packet_getsockopt(struct sock
+ spin_unlock_bh(&sk->sk_receive_queue.lock);
+ st.tp_packets += st.tp_drops;
+
+- if (copy_to_user(optval, &st, len))
+- return -EFAULT;
++ data = &st;
++ break;
++ case PACKET_AUXDATA:
++ if (len > sizeof(int))
++ len = sizeof(int);
++ val = po->auxdata;
++
++ data = &val;
+ break;
+- }
+ default:
+ return -ENOPROTOOPT;
+ }
+
+ if (put_user(len, optlen))
+ return -EFAULT;
++ if (copy_to_user(optval, data, len))
++ return -EFAULT;
+ return 0;
+ }
+