#include <linux/sched.h>
 #include <linux/key.h>
 #include <linux/xfrm.h>
+#include <net/flow.h>
 
 struct ctl_table;
 
  *     Deallocate security structure.
  * @sk_clone_security:
  *     Clone/copy security structure.
- * @sk_getsid:
- *     Retrieve the LSM-specific sid for the sock to enable caching of network
+ * @sk_getsecid:
+ *     Retrieve the LSM-specific secid for the sock to enable caching of network
  *     authorizations.
  *
  * Security hooks for XFRM operations.
  *     Return 1 if there is a match.
  * @xfrm_decode_session:
  *     @skb points to skb to decode.
- *     @fl points to the flow key to set.
- *     Return 0 if successful decoding.
+ *     @secid points to the flow key secid to set.
+ *     @ckall says if all xfrms used should be checked for same secid.
+ *     Return 0 if ckall is zero or all xfrms used have the same secid.
  *
  * Security hooks affecting all Key Management operations
  *
        int (*sk_alloc_security) (struct sock *sk, int family, gfp_t priority);
        void (*sk_free_security) (struct sock *sk);
        void (*sk_clone_security) (const struct sock *sk, struct sock *newsk);
-       unsigned int (*sk_getsid) (struct sock *sk, struct flowi *fl, u8 dir);
+       void (*sk_getsecid) (struct sock *sk, u32 *secid);
 #endif /* CONFIG_SECURITY_NETWORK */
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
        int (*xfrm_state_pol_flow_match)(struct xfrm_state *x,
                        struct xfrm_policy *xp, struct flowi *fl);
        int (*xfrm_flow_state_match)(struct flowi *fl, struct xfrm_state *xfrm);
-       int (*xfrm_decode_session)(struct sk_buff *skb, struct flowi *fl);
+       int (*xfrm_decode_session)(struct sk_buff *skb, u32 *secid, int ckall);
 #endif /* CONFIG_SECURITY_NETWORK_XFRM */
 
        /* key management security hooks */
        return security_ops->sk_clone_security(sk, newsk);
 }
 
-static inline unsigned int security_sk_sid(struct sock *sk, struct flowi *fl, u8 dir)
+static inline void security_sk_classify_flow(struct sock *sk, struct flowi *fl)
 {
-       return security_ops->sk_getsid(sk, fl, dir);
+       security_ops->sk_getsecid(sk, &fl->secid);
 }
 #else  /* CONFIG_SECURITY_NETWORK */
 static inline int security_unix_stream_connect(struct socket * sock,
 {
 }
 
-static inline unsigned int security_sk_sid(struct sock *sk, struct flowi *fl, u8 dir)
+static inline void security_sk_classify_flow(struct sock *sk, struct flowi *fl)
 {
-       return 0;
 }
 #endif /* CONFIG_SECURITY_NETWORK */
 
        return security_ops->xfrm_flow_state_match(fl, xfrm);
 }
 
-static inline int security_xfrm_decode_session(struct sk_buff *skb, struct flowi *fl)
+static inline int security_xfrm_decode_session(struct sk_buff *skb, u32 *secid)
+{
+       return security_ops->xfrm_decode_session(skb, secid, 1);
+}
+
+static inline void security_skb_classify_flow(struct sk_buff *skb, struct flowi *fl)
 {
-       return security_ops->xfrm_decode_session(skb, fl);
+       int rc = security_ops->xfrm_decode_session(skb, &fl->secid, 0);
+
+       BUG_ON(rc);
 }
 #else  /* CONFIG_SECURITY_NETWORK_XFRM */
 static inline int security_xfrm_policy_alloc(struct xfrm_policy *xp, struct xfrm_user_sec_ctx *sec_ctx)
        return 1;
 }
 
-static inline int security_xfrm_decode_session(struct sk_buff *skb, struct flowi *fl)
+static inline int security_xfrm_decode_session(struct sk_buff *skb, u32 *secid)
 {
        return 0;
 }
 
+static inline void security_skb_classify_flow(struct sk_buff *skb, struct flowi *fl)
+{
+}
+
 #endif /* CONFIG_SECURITY_NETWORK_XFRM */
 
 #ifdef CONFIG_KEYS
 
 #include <linux/route.h>
 #include <linux/ip.h>
 #include <linux/cache.h>
+#include <linux/security.h>
 
 #ifndef __KERNEL__
 #warning This file is not supposed to be used outside of kernel.
                ip_rt_put(*rp);
                *rp = NULL;
        }
+       security_sk_classify_flow(sk, &fl);
        return ip_route_output_flow(rp, &fl, sk, 0);
 }
 
                fl.proto = protocol;
                ip_rt_put(*rp);
                *rp = NULL;
+               security_sk_classify_flow(sk, &fl);
                return ip_route_output_flow(rp, &fl, sk, 0);
        }
        return 0;
 
                                     }
                          };
 
+       security_skb_classify_flow(skb, &fl);
        if (ip_route_output_flow(&rt, &fl, sk, 0)) {
                IP_INC_STATS_BH(IPSTATS_MIB_OUTNOROUTES);
                return NULL;
 
        fl.oif = sk->sk_bound_dev_if;
        fl.fl_ip_dport = usin->sin6_port;
        fl.fl_ip_sport = inet->sport;
+       security_sk_classify_flow(sk, &fl);
 
        if (np->opt != NULL && np->opt->srcrt != NULL) {
                const struct rt0_hdr *rt0 = (struct rt0_hdr *)np->opt->srcrt;
                        fl.oif = sk->sk_bound_dev_if;
                        fl.fl_ip_dport = inet->dport;
                        fl.fl_ip_sport = inet->sport;
+                       security_sk_classify_flow(sk, &fl);
 
                        err = ip6_dst_lookup(sk, &dst, &fl);
                        if (err) {
        fl.oif = ireq6->iif;
        fl.fl_ip_dport = inet_rsk(req)->rmt_port;
        fl.fl_ip_sport = inet_sk(sk)->sport;
+       security_sk_classify_flow(sk, &fl);
 
        if (dst == NULL) {
                opt = np->opt;
        fl.oif = inet6_iif(rxskb);
        fl.fl_ip_dport = dh->dccph_dport;
        fl.fl_ip_sport = dh->dccph_sport;
+       security_skb_classify_flow(rxskb, &fl);
 
        /* sk = NULL, but it is safe for now. RST socket required. */
        if (!ip6_dst_lookup(NULL, &skb->dst, &fl)) {
        fl.oif = inet6_iif(rxskb);
        fl.fl_ip_dport = dh->dccph_dport;
        fl.fl_ip_sport = dh->dccph_sport;
+       security_skb_classify_flow(rxskb, &fl);
 
        if (!ip6_dst_lookup(NULL, &skb->dst, &fl)) {
                if (xfrm_lookup(&skb->dst, &fl, NULL, 0) >= 0) {
                fl.oif = sk->sk_bound_dev_if;
                fl.fl_ip_dport = inet_rsk(req)->rmt_port;
                fl.fl_ip_sport = inet_sk(sk)->sport;
+               security_sk_classify_flow(sk, &fl);
 
                if (ip6_dst_lookup(sk, &dst, &fl))
                        goto out;
 
                },
        };
                                                
+       security_sk_classify_flow(sk, &fl);
        err = ip_route_output_flow(&rt, &fl, sk, 0);
 }
        if (!err)
 
                                                .saddr = rt->rt_spec_dst,
                                                .tos = RT_TOS(skb->nh.iph->tos) } },
                                    .proto = IPPROTO_ICMP };
+               security_skb_classify_flow(skb, &fl);
                if (ip_route_output_key(&rt, &fl))
                        goto out_unlock;
        }
                                }
                        }
                };
+               security_skb_classify_flow(skb_in, &fl);
                if (ip_route_output_key(&rt, &fl))
                        goto out_unlock;
        }
 
                                       { .sport = inet_sk(sk)->sport,
                                         .dport = ireq->rmt_port } } };
 
+       security_sk_classify_flow(sk, &fl);
        if (ip_route_output_flow(&rt, &fl, sk, 0)) {
                IP_INC_STATS_BH(IPSTATS_MIB_OUTNOROUTES);
                return NULL;
 
                         * keep trying until route appears or the connection times
                         * itself out.
                         */
+                       security_sk_classify_flow(sk, &fl);
                        if (ip_route_output_flow(&rt, &fl, sk, 0))
                                goto no_route;
                }
                                               { .sport = skb->h.th->dest,
                                                 .dport = skb->h.th->source } },
                                    .proto = sk->sk_protocol };
+               security_skb_classify_flow(skb, &fl);
                if (ip_route_output_key(&rt, &fl))
                        return;
        }
 
        fl.proto = IPPROTO_TCP;
        fl.fl_ip_sport = tcph->dest;
        fl.fl_ip_dport = tcph->source;
+       security_skb_classify_flow(skb, &fl);
 
        xfrm_lookup((struct dst_entry **)&rt, &fl, NULL, 0);
 
 
                if (!inet->hdrincl)
                        raw_probe_proto_opt(&fl, msg);
 
+               security_sk_classify_flow(sk, &fl);
                err = ip_route_output_flow(&rt, &fl, sk, !(msg->msg_flags&MSG_DONTWAIT));
        }
        if (err)
 
                                    .uli_u = { .ports =
                                               { .sport = skb->h.th->dest,
                                                 .dport = skb->h.th->source } } };
+               security_sk_classify_flow(sk, &fl);
                if (ip_route_output_key(&rt, &fl)) {
                        reqsk_free(req);
                        goto out; 
 
                                    .uli_u = { .ports =
                                               { .sport = inet->sport,
                                                 .dport = dport } } };
+               security_sk_classify_flow(sk, &fl);
                err = ip_route_output_flow(&rt, &fl, sk, !(msg->msg_flags&MSG_DONTWAIT));
                if (err)
                        goto out;
 
                fl.oif = sk->sk_bound_dev_if;
                fl.fl_ip_dport = inet->dport;
                fl.fl_ip_sport = inet->sport;
+               security_sk_classify_flow(sk, &fl);
 
                if (np->opt && np->opt->srcrt) {
                        struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
 
        if (!fl.oif && (addr_type&IPV6_ADDR_MULTICAST))
                fl.oif = np->mcast_oif;
 
+       security_sk_classify_flow(sk, &fl);
+
        if (flowlabel) {
                if (flowlabel->opt && flowlabel->opt->srcrt) {
                        struct rt0_hdr *rt0 = (struct rt0_hdr *) flowlabel->opt->srcrt;
 
        fl.oif = iif;
        fl.fl_icmp_type = type;
        fl.fl_icmp_code = code;
+       security_skb_classify_flow(skb, &fl);
 
        if (icmpv6_xmit_lock())
                return;
                ipv6_addr_copy(&fl.fl6_src, saddr);
        fl.oif = skb->dev->ifindex;
        fl.fl_icmp_type = ICMPV6_ECHO_REPLY;
+       security_skb_classify_flow(skb, &fl);
 
        if (icmpv6_xmit_lock())
                return;
 
        fl.oif = sk->sk_bound_dev_if;
        fl.fl_ip_sport = inet->sport;
        fl.fl_ip_dport = inet->dport;
+       security_sk_classify_flow(sk, &fl);
 
        if (np->opt && np->opt->srcrt) {
                struct rt0_hdr *rt0 = (struct rt0_hdr *)np->opt->srcrt;
 
        fl->proto               = IPPROTO_ICMPV6;
        fl->fl_icmp_type        = type;
        fl->fl_icmp_code        = 0;
+       security_sk_classify_flow(ndisc_socket->sk, fl);
 }
 
 static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
 
        ipv6_addr_copy(&fl.fl6_dst, &oip6h->saddr);
        fl.fl_ip_sport = otcph.dest;
        fl.fl_ip_dport = otcph.source;
+       security_skb_classify_flow(oldskb, &fl);
        dst = ip6_route_output(NULL, &fl);
        if (dst == NULL)
                return;
 
 
        if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst))
                fl.oif = np->mcast_oif;
+       security_sk_classify_flow(sk, &fl);
 
        err = ip6_dst_lookup(sk, &dst, &fl);
        if (err)
 
                final_p = &final;
        }
 
+       security_sk_classify_flow(sk, &fl);
+
        err = ip6_dst_lookup(sk, &dst, &fl);
        if (err)
                goto failure;
                        fl.oif = sk->sk_bound_dev_if;
                        fl.fl_ip_dport = inet->dport;
                        fl.fl_ip_sport = inet->sport;
+                       security_skb_classify_flow(skb, &fl);
 
                        if ((err = ip6_dst_lookup(sk, &dst, &fl))) {
                                sk->sk_err_soft = -err;
        fl.oif = treq->iif;
        fl.fl_ip_dport = inet_rsk(req)->rmt_port;
        fl.fl_ip_sport = inet_sk(sk)->sport;
+       security_sk_classify_flow(sk, &fl);
 
        if (dst == NULL) {
                opt = np->opt;
        fl.oif = inet6_iif(skb);
        fl.fl_ip_dport = t1->dest;
        fl.fl_ip_sport = t1->source;
+       security_skb_classify_flow(skb, &fl);
 
        /* sk = NULL, but it is safe for now. RST socket required. */
        if (!ip6_dst_lookup(NULL, &buff->dst, &fl)) {
        fl.oif = inet6_iif(skb);
        fl.fl_ip_dport = t1->dest;
        fl.fl_ip_sport = t1->source;
+       security_skb_classify_flow(skb, &fl);
 
        if (!ip6_dst_lookup(NULL, &buff->dst, &fl)) {
                if (xfrm_lookup(&buff->dst, &fl, NULL, 0) >= 0) {
                fl.oif = sk->sk_bound_dev_if;
                fl.fl_ip_dport = inet_rsk(req)->rmt_port;
                fl.fl_ip_sport = inet_sk(sk)->sport;
+               security_sk_classify_flow(sk, &fl);
 
                if (ip6_dst_lookup(sk, &dst, &fl))
                        goto out;
 
                connected = 0;
        }
 
+       security_sk_classify_flow(sk, fl);
+
        err = ip6_sk_dst_lookup(sk, &dst, fl);
        if (err)
                goto out;
 
        u16 family;
        u8 dir = policy_to_flow_dir(XFRM_POLICY_OUT);
 
-       fl->secid = security_sk_sid(sk, fl, dir);
 restart:
        genid = atomic_read(&flow_cache_genid);
        policy = NULL;
                return -EAFNOSUPPORT;
 
        afinfo->decode_session(skb, fl);
-       err = security_xfrm_decode_session(skb, fl);
+       err = security_xfrm_decode_session(skb, &fl->secid);
        xfrm_policy_put_afinfo(afinfo);
        return err;
 }
 
 {
 }
 
-static unsigned int dummy_sk_getsid(struct sock *sk, struct flowi *fl, u8 dir)
+static inline void dummy_sk_getsecid(struct sock *sk, u32 *secid)
 {
-       return 0;
 }
 #endif /* CONFIG_SECURITY_NETWORK */
 
        return 1;
 }
 
-static int dummy_xfrm_decode_session(struct sk_buff *skb, struct flowi *fl)
+static int dummy_xfrm_decode_session(struct sk_buff *skb, u32 *fl, int ckall)
 {
        return 0;
 }
        set_to_dummy_if_null(ops, sk_alloc_security);
        set_to_dummy_if_null(ops, sk_free_security);
        set_to_dummy_if_null(ops, sk_clone_security);
-       set_to_dummy_if_null(ops, sk_getsid);
+       set_to_dummy_if_null(ops, sk_getsecid);
  #endif        /* CONFIG_SECURITY_NETWORK */
 #ifdef  CONFIG_SECURITY_NETWORK_XFRM
        set_to_dummy_if_null(ops, xfrm_policy_alloc_security);
 
        newssec->peer_sid = ssec->peer_sid;
 }
 
-static unsigned int selinux_sk_getsid_security(struct sock *sk, struct flowi *fl, u8 dir)
+static void selinux_sk_getsecid(struct sock *sk, u32 *secid)
 {
        if (!sk)
-               return selinux_no_sk_sid(fl);
+               *secid = SECINITSID_ANY_SOCKET;
        else {
                struct sk_security_struct *sksec = sk->sk_security;
 
-               return sksec->sid;
+               *secid = sksec->sid;
        }
 }
 
        .sk_alloc_security =            selinux_sk_alloc_security,
        .sk_free_security =             selinux_sk_free_security,
        .sk_clone_security =            selinux_sk_clone_security,
-       .sk_getsid =                    selinux_sk_getsid_security,
+       .sk_getsecid =                  selinux_sk_getsecid,
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
        .xfrm_policy_alloc_security =   selinux_xfrm_policy_alloc,
 
 int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x,
                        struct xfrm_policy *xp, struct flowi *fl);
 int selinux_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm);
-int selinux_xfrm_decode_session(struct sk_buff *skb, struct flowi *fl);
+int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *fl, int ckall);
 
 
 /*
        return SOCK_INODE(sk->sk_socket)->i_security;
 }
 
-
-static inline u32 selinux_no_sk_sid(struct flowi *fl)
-{
-       /* NOTE: no sock occurs on ICMP reply, forwards, ... */
-       /* icmp_reply: authorize as kernel packet */
-       if (fl && fl->proto == IPPROTO_ICMP) {
-               return SECINITSID_KERNEL;
-       }
-
-       return SECINITSID_ANY_SOCKET;
-}
-
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
 int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb,
                        struct avc_audit_data *ad);
 
  * LSM hook implementation that determines the sid for the session.
  */
 
-int selinux_xfrm_decode_session(struct sk_buff *skb, struct flowi *fl)
+int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
 {
        struct sec_path *sp;
 
-       fl->secid = SECSID_NULL;
+       *sid = SECSID_NULL;
 
        if (skb == NULL)
                return 0;
                                struct xfrm_sec_ctx *ctx = x->security;
 
                                if (!sid_set) {
-                                       fl->secid = ctx->ctx_sid;
+                                       *sid = ctx->ctx_sid;
                                        sid_set = 1;
+
+                                       if (!ckall)
+                                               break;
                                }
-                               else if (fl->secid != ctx->ctx_sid)
+                               else if (*sid != ctx->ctx_sid)
                                        return -EINVAL;
                        }
                }