void                    (*destructor)(struct xfrm_state *);
        int                     (*input)(struct xfrm_state *, struct sk_buff *skb);
        int                     (*output)(struct xfrm_state *, struct sk_buff *pskb);
+       int                     (*reject)(struct xfrm_state *, struct sk_buff *, struct flowi *);
        int                     (*hdr_offset)(struct xfrm_state *, struct sk_buff *, u8 **);
        xfrm_address_t          *(*local_addr)(struct xfrm_state *, xfrm_address_t *);
        xfrm_address_t          *(*remote_addr)(struct xfrm_state *, xfrm_address_t *);
 
 }
 EXPORT_SYMBOL(xfrm_lookup);
 
+static inline int
+xfrm_secpath_reject(int idx, struct sk_buff *skb, struct flowi *fl)
+{
+       struct xfrm_state *x;
+       int err;
+
+       if (!skb->sp || idx < 0 || idx >= skb->sp->len)
+               return 0;
+       x = skb->sp->xvec[idx];
+       if (!x->type->reject)
+               return 0;
+       xfrm_state_hold(x);
+       err = x->type->reject(x, skb, fl);
+       xfrm_state_put(x);
+       return err;
+}
+
 /* When skb is transformed back to its "native" form, we have to
  * check policy restrictions. At the moment we make this in maximally
  * stupid way. Shame on me. :-) Of course, connected sockets must
                  xfrm_state_addr_cmp(tmpl, x, family));
 }
 
+/*
+ * 0 or more than 0 is returned when validation is succeeded (either bypass
+ * because of optional transport mode, or next index of the mathced secpath
+ * state with the template.
+ * -1 is returned when no matching template is found.
+ * Otherwise "-2 - errored_index" is returned.
+ */
 static inline int
 xfrm_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int start,
               unsigned short family)
        for (; idx < sp->len; idx++) {
                if (xfrm_state_ok(tmpl, sp->xvec[idx], family))
                        return ++idx;
-               if (sp->xvec[idx]->props.mode != XFRM_MODE_TRANSPORT)
+               if (sp->xvec[idx]->props.mode != XFRM_MODE_TRANSPORT) {
+                       if (start == -1)
+                               start = -2-idx;
                        break;
+               }
        }
        return start;
 }
 }
 EXPORT_SYMBOL(xfrm_decode_session);
 
-static inline int secpath_has_nontransport(struct sec_path *sp, int k)
+static inline int secpath_has_nontransport(struct sec_path *sp, int k, int *idxp)
 {
        for (; k < sp->len; k++) {
-               if (sp->xvec[k]->props.mode != XFRM_MODE_TRANSPORT)
+               if (sp->xvec[k]->props.mode != XFRM_MODE_TRANSPORT) {
+                       if (idxp)
+                               *idxp = k;
                        return 1;
+               }
        }
 
        return 0;
        struct xfrm_policy *pol;
        struct flowi fl;
        u8 fl_dir = policy_to_flow_dir(dir);
+       int xerr_idx = -1;
+       int *xerr_idxp = &xerr_idx;
 
        if (xfrm_decode_session(skb, &fl, family) < 0)
                return 0;
                pol = flow_cache_lookup(&fl, family, fl_dir,
                                        xfrm_policy_lookup);
 
-       if (!pol)
-               return !skb->sp || !secpath_has_nontransport(skb->sp, 0);
+       if (!pol) {
+               if (skb->sp && secpath_has_nontransport(skb->sp, 0, xerr_idxp)) {
+                       xfrm_secpath_reject(xerr_idx, skb, &fl);
+                       return 0;
+               }
+               return 1;
+       }
 
        pol->curlft.use_time = (unsigned long)xtime.tv_sec;
 
                 */
                for (i = pol->xfrm_nr-1, k = 0; i >= 0; i--) {
                        k = xfrm_policy_ok(pol->xfrm_vec+i, sp, k, family);
-                       if (k < 0)
+                       if (k < 0) {
+                               if (k < -1 && xerr_idxp)
+                                       *xerr_idxp = -(2+k);
                                goto reject;
+                       }
                }
 
-               if (secpath_has_nontransport(sp, k))
+               if (secpath_has_nontransport(sp, k, xerr_idxp))
                        goto reject;
 
                xfrm_pol_put(pol);
        }
 
 reject:
+       xfrm_secpath_reject(xerr_idx, skb, &fl);
        xfrm_pol_put(pol);
        return 0;
 }