ipv4: PKTINFO doesnt need dst reference
[pandora-kernel.git] / net / ipv4 / ip_sockglue.c
index 8905e92..80d5fa4 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/netfilter.h>
 #include <linux/route.h>
 #include <linux/mroute.h>
+#include <net/inet_ecn.h>
 #include <net/route.h>
 #include <net/xfrm.h>
 #include <net/compat.h>
 /*
  *     SOL_IP control messages.
  */
+#define PKTINFO_SKB_CB(__skb) ((struct in_pktinfo *)((__skb)->cb))
 
 static void ip_cmsg_recv_pktinfo(struct msghdr *msg, struct sk_buff *skb)
 {
-       struct in_pktinfo info;
-       struct rtable *rt = skb_rtable(skb);
+       struct in_pktinfo info = *PKTINFO_SKB_CB(skb);
 
        info.ipi_addr.s_addr = ip_hdr(skb)->daddr;
-       if (rt) {
-               info.ipi_ifindex = rt->rt_iif;
-               info.ipi_spec_dst.s_addr = rt->rt_spec_dst;
-       } else {
-               info.ipi_ifindex = 0;
-               info.ipi_spec_dst.s_addr = 0;
-       }
 
        put_cmsg(msg, SOL_IP, IP_PKTINFO, sizeof(info), &info);
 }
@@ -578,8 +572,8 @@ static int do_ip_setsockopt(struct sock *sk, int level,
                break;
        case IP_TOS:    /* This sets both TOS and Precedence */
                if (sk->sk_type == SOCK_STREAM) {
-                       val &= ~3;
-                       val |= inet->tos & 3;
+                       val &= ~INET_ECN_MASK;
+                       val |= inet->tos & INET_ECN_MASK;
                }
                if (inet->tos != val) {
                        inet->tos = val;
@@ -961,7 +955,7 @@ mc_msf_out:
                break;
 
        case IP_TRANSPARENT:
-               if (!capable(CAP_NET_ADMIN)) {
+               if (!!val && !capable(CAP_NET_RAW) && !capable(CAP_NET_ADMIN)) {
                        err = -EPERM;
                        break;
                }
@@ -991,20 +985,28 @@ e_inval:
 }
 
 /**
- * ip_queue_rcv_skb - Queue an skb into sock receive queue
+ * ipv4_pktinfo_prepare - transfert some info from rtable to skb
  * @sk: socket
  * @skb: buffer
  *
- * Queues an skb into socket receive queue. If IP_CMSG_PKTINFO option
- * is not set, we drop skb dst entry now, while dst cache line is hot.
+ * To support IP_CMSG_PKTINFO option, we store rt_iif and rt_spec_dst
+ * in skb->cb[] before dst drop.
+ * This way, receiver doesnt make cache line misses to read rtable.
  */
-int ip_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
+void ipv4_pktinfo_prepare(struct sk_buff *skb)
 {
-       if (!(inet_sk(sk)->cmsg_flags & IP_CMSG_PKTINFO))
-               skb_dst_drop(skb);
-       return sock_queue_rcv_skb(sk, skb);
+       struct in_pktinfo *pktinfo = PKTINFO_SKB_CB(skb);
+       const struct rtable *rt = skb_rtable(skb);
+
+       if (rt) {
+               pktinfo->ipi_ifindex = rt->rt_iif;
+               pktinfo->ipi_spec_dst.s_addr = rt->rt_spec_dst;
+       } else {
+               pktinfo->ipi_ifindex = 0;
+               pktinfo->ipi_spec_dst.s_addr = 0;
+       }
+       skb_dst_drop(skb);
 }
-EXPORT_SYMBOL(ip_queue_rcv_skb);
 
 int ip_setsockopt(struct sock *sk, int level,
                int optname, char __user *optval, unsigned int optlen)