fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / net / ipv4 / ip_sockglue.c
index 2bf8d78..57d4bae 100644 (file)
@@ -17,7 +17,6 @@
  *             Mike McLagan    :       Routing by source
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/mm.h>
@@ -50,6 +49,7 @@
 #define IP_CMSG_TOS            4
 #define IP_CMSG_RECVOPTS       8
 #define IP_CMSG_RETOPTS                16
+#define IP_CMSG_PASSSEC                32
 
 /*
  *     SOL_IP control messages.
@@ -109,6 +109,24 @@ static void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb)
        put_cmsg(msg, SOL_IP, IP_RETOPTS, opt->optlen, opt->__data);
 }
 
+static void ip_cmsg_recv_security(struct msghdr *msg, struct sk_buff *skb)
+{
+       char *secdata;
+       u32 seclen, secid;
+       int err;
+
+       err = security_socket_getpeersec_dgram(NULL, skb, &secid);
+       if (err)
+               return;
+
+       err = security_secid_to_secctx(secid, &secdata, &seclen);
+       if (err)
+               return;
+
+       put_cmsg(msg, SOL_IP, SCM_SECURITY, seclen, secdata);
+       security_release_secctx(secdata, seclen);
+}
+
 
 void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)
 {
@@ -138,6 +156,11 @@ void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)
 
        if (flags & 1)
                ip_cmsg_recv_retopts(msg, skb);
+       if ((flags>>=1) == 0)
+               return;
+
+       if (flags & 1)
+               ip_cmsg_recv_security(msg, skb);
 }
 
 int ip_cmsg_send(struct msghdr *msg, struct ipcm_cookie *ipc)
@@ -231,7 +254,7 @@ int ip_ra_control(struct sock *sk, unsigned char on, void (*destructor)(struct s
 }
 
 void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err, 
-                  u16 port, u32 info, u8 *payload)
+                  __be16 port, u32 info, u8 *payload)
 {
        struct inet_sock *inet = inet_sk(sk);
        struct sock_exterr_skb *serr;
@@ -260,7 +283,7 @@ void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
                kfree_skb(skb);
 }
 
-void ip_local_error(struct sock *sk, int err, u32 daddr, u16 port, u32 info)
+void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 info)
 {
        struct inet_sock *inet = inet_sk(sk);
        struct sock_exterr_skb *serr;
@@ -332,7 +355,7 @@ int ip_recv_error(struct sock *sk, struct msghdr *msg, int len)
        sin = (struct sockaddr_in *)msg->msg_name;
        if (sin) {
                sin->sin_family = AF_INET;
-               sin->sin_addr.s_addr = *(u32*)(skb->nh.raw + serr->addr_offset);
+               sin->sin_addr.s_addr = *(__be32*)(skb->nh.raw + serr->addr_offset);
                sin->sin_port = serr->port;
                memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
        }
@@ -380,20 +403,19 @@ out:
  *     an IP socket.
  */
 
-int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval, int optlen)
+static int do_ip_setsockopt(struct sock *sk, int level,
+               int optname, char __user *optval, int optlen)
 {
        struct inet_sock *inet = inet_sk(sk);
        int val=0,err;
 
-       if (level != SOL_IP)
-               return -ENOPROTOOPT;
-
        if (((1<<optname) & ((1<<IP_PKTINFO) | (1<<IP_RECVTTL) | 
                            (1<<IP_RECVOPTS) | (1<<IP_RECVTOS) | 
                            (1<<IP_RETOPTS) | (1<<IP_TOS) | 
                            (1<<IP_TTL) | (1<<IP_HDRINCL) | 
                            (1<<IP_MTU_DISCOVER) | (1<<IP_RECVERR) | 
-                           (1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND))) || 
+                           (1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND) |
+                           (1<<IP_PASSSEC))) ||
                                optname == IP_MULTICAST_TTL || 
                                optname == IP_MULTICAST_LOOP) { 
                if (optlen >= sizeof(int)) {
@@ -478,6 +500,12 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval,
                        else
                                inet->cmsg_flags &= ~IP_CMSG_RETOPTS;
                        break;
+               case IP_PASSSEC:
+                       if (val)
+                               inet->cmsg_flags |= IP_CMSG_PASSSEC;
+                       else
+                               inet->cmsg_flags &= ~IP_CMSG_PASSSEC;
+                       break;
                case IP_TOS:    /* This sets both TOS and Precedence */
                        if (sk->sk_type == SOCK_STREAM) {
                                val &= ~3;
@@ -849,12 +877,7 @@ mc_msf_out:
                        break;
 
                default:
-#ifdef CONFIG_NETFILTER
-                       err = nf_setsockopt(sk, PF_INET, optname, optval, 
-                                           optlen);
-#else
                        err = -ENOPROTOOPT;
-#endif
                        break;
        }
        release_sock(sk);
@@ -865,12 +888,68 @@ e_inval:
        return -EINVAL;
 }
 
+int ip_setsockopt(struct sock *sk, int level,
+               int optname, char __user *optval, int optlen)
+{
+       int err;
+
+       if (level != SOL_IP)
+               return -ENOPROTOOPT;
+
+       err = do_ip_setsockopt(sk, level, optname, optval, optlen);
+#ifdef CONFIG_NETFILTER
+       /* we need to exclude all possible ENOPROTOOPTs except default case */
+       if (err == -ENOPROTOOPT && optname != IP_HDRINCL &&
+               optname != IP_IPSEC_POLICY && optname != IP_XFRM_POLICY
+#ifdef CONFIG_IP_MROUTE
+               && (optname < MRT_BASE || optname > (MRT_BASE + 10))
+#endif
+          ) {
+               lock_sock(sk);
+               err = nf_setsockopt(sk, PF_INET, optname, optval, optlen);
+               release_sock(sk);
+       }
+#endif
+       return err;
+}
+
+#ifdef CONFIG_COMPAT
+int compat_ip_setsockopt(struct sock *sk, int level, int optname,
+                        char __user *optval, int optlen)
+{
+       int err;
+
+       if (level != SOL_IP)
+               return -ENOPROTOOPT;
+
+       err = do_ip_setsockopt(sk, level, optname, optval, optlen);
+#ifdef CONFIG_NETFILTER
+       /* we need to exclude all possible ENOPROTOOPTs except default case */
+       if (err == -ENOPROTOOPT && optname != IP_HDRINCL &&
+           optname != IP_IPSEC_POLICY && optname != IP_XFRM_POLICY
+#ifdef CONFIG_IP_MROUTE
+           && (optname < MRT_BASE || optname > (MRT_BASE + 10))
+#endif
+          ) {
+               lock_sock(sk);
+               err = compat_nf_setsockopt(sk, PF_INET, optname,
+                                          optval, optlen);
+               release_sock(sk);
+       }
+#endif
+       return err;
+}
+
+EXPORT_SYMBOL(compat_ip_setsockopt);
+#endif
+
 /*
  *     Get the options. Note for future reference. The GET of IP options gets the
  *     _received_ ones. The set sets the _sent_ ones.
  */
 
-int ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen)
+static int do_ip_getsockopt(struct sock *sk, int level, int optname,
+               char __user *optval, int __user *optlen)
 {
        struct inet_sock *inet = inet_sk(sk);
        int val;
@@ -932,6 +1011,9 @@ int ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval,
                case IP_RETOPTS:
                        val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0;
                        break;
+               case IP_PASSSEC:
+                       val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0;
+                       break;
                case IP_TOS:
                        val = inet->tos;
                        break;
@@ -1051,17 +1133,8 @@ int ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval,
                        val = inet->freebind; 
                        break; 
                default:
-#ifdef CONFIG_NETFILTER
-                       val = nf_getsockopt(sk, PF_INET, optname, optval, 
-                                           &len);
-                       release_sock(sk);
-                       if (val >= 0)
-                               val = put_user(len, optlen);
-                       return val;
-#else
                        release_sock(sk);
                        return -ENOPROTOOPT;
-#endif
        }
        release_sock(sk);
        
@@ -1082,6 +1155,67 @@ int ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval,
        return 0;
 }
 
+int ip_getsockopt(struct sock *sk, int level,
+               int optname, char __user *optval, int __user *optlen)
+{
+       int err;
+
+       err = do_ip_getsockopt(sk, level, optname, optval, optlen);
+#ifdef CONFIG_NETFILTER
+       /* we need to exclude all possible ENOPROTOOPTs except default case */
+       if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS
+#ifdef CONFIG_IP_MROUTE
+               && (optname < MRT_BASE || optname > MRT_BASE+10)
+#endif
+          ) {
+               int len;
+
+               if(get_user(len,optlen))
+                       return -EFAULT;
+
+               lock_sock(sk);
+               err = nf_getsockopt(sk, PF_INET, optname, optval,
+                               &len);
+               release_sock(sk);
+               if (err >= 0)
+                       err = put_user(len, optlen);
+               return err;
+       }
+#endif
+       return err;
+}
+
+#ifdef CONFIG_COMPAT
+int compat_ip_getsockopt(struct sock *sk, int level, int optname,
+                        char __user *optval, int __user *optlen)
+{
+       int err = do_ip_getsockopt(sk, level, optname, optval, optlen);
+#ifdef CONFIG_NETFILTER
+       /* we need to exclude all possible ENOPROTOOPTs except default case */
+       if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS
+#ifdef CONFIG_IP_MROUTE
+           && (optname < MRT_BASE || optname > MRT_BASE+10)
+#endif
+          ) {
+               int len;
+
+               if (get_user(len, optlen))
+                       return -EFAULT;
+
+               lock_sock(sk);
+               err = compat_nf_getsockopt(sk, PF_INET, optname, optval, &len);
+               release_sock(sk);
+               if (err >= 0)
+                       err = put_user(len, optlen);
+               return err;
+       }
+#endif
+       return err;
+}
+
+EXPORT_SYMBOL(compat_ip_getsockopt);
+#endif
+
 EXPORT_SYMBOL(ip_cmsg_recv);
 
 EXPORT_SYMBOL(ip_getsockopt);