Merge tag 'driver-core-4.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
[deliverable/linux.git] / net / netfilter / nf_conntrack_ecache.c
index 4e78c57b818f7d2cc387c793fe5a100e03fe067e..d28011b428455d83d1230210982699cb8774c55b 100644 (file)
@@ -113,6 +113,60 @@ static void ecache_work(struct work_struct *work)
                schedule_delayed_work(&ctnet->ecache_dwork, delay);
 }
 
+int nf_conntrack_eventmask_report(unsigned int eventmask, struct nf_conn *ct,
+                                 u32 portid, int report)
+{
+       int ret = 0;
+       struct net *net = nf_ct_net(ct);
+       struct nf_ct_event_notifier *notify;
+       struct nf_conntrack_ecache *e;
+
+       rcu_read_lock();
+       notify = rcu_dereference(net->ct.nf_conntrack_event_cb);
+       if (!notify)
+               goto out_unlock;
+
+       e = nf_ct_ecache_find(ct);
+       if (!e)
+               goto out_unlock;
+
+       if (nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct)) {
+               struct nf_ct_event item = {
+                       .ct     = ct,
+                       .portid = e->portid ? e->portid : portid,
+                       .report = report
+               };
+               /* This is a resent of a destroy event? If so, skip missed */
+               unsigned long missed = e->portid ? 0 : e->missed;
+
+               if (!((eventmask | missed) & e->ctmask))
+                       goto out_unlock;
+
+               ret = notify->fcn(eventmask | missed, &item);
+               if (unlikely(ret < 0 || missed)) {
+                       spin_lock_bh(&ct->lock);
+                       if (ret < 0) {
+                               /* This is a destroy event that has been
+                                * triggered by a process, we store the PORTID
+                                * to include it in the retransmission.
+                                */
+                               if (eventmask & (1 << IPCT_DESTROY) &&
+                                   e->portid == 0 && portid != 0)
+                                       e->portid = portid;
+                               else
+                                       e->missed |= eventmask;
+                       } else {
+                               e->missed &= ~missed;
+                       }
+                       spin_unlock_bh(&ct->lock);
+               }
+       }
+out_unlock:
+       rcu_read_unlock();
+       return ret;
+}
+EXPORT_SYMBOL_GPL(nf_conntrack_eventmask_report);
+
 /* deliver cached events and clear cache entry - must be called with locally
  * disabled softirqs */
 void nf_ct_deliver_cached_events(struct nf_conn *ct)
@@ -167,6 +221,36 @@ out_unlock:
 }
 EXPORT_SYMBOL_GPL(nf_ct_deliver_cached_events);
 
+void nf_ct_expect_event_report(enum ip_conntrack_expect_events event,
+                              struct nf_conntrack_expect *exp,
+                              u32 portid, int report)
+
+{
+       struct net *net = nf_ct_exp_net(exp);
+       struct nf_exp_event_notifier *notify;
+       struct nf_conntrack_ecache *e;
+
+       rcu_read_lock();
+       notify = rcu_dereference(net->ct.nf_expect_event_cb);
+       if (!notify)
+               goto out_unlock;
+
+       e = nf_ct_ecache_find(exp->master);
+       if (!e)
+               goto out_unlock;
+
+       if (e->expmask & (1 << event)) {
+               struct nf_exp_event item = {
+                       .exp    = exp,
+                       .portid = portid,
+                       .report = report
+               };
+               notify->fcn(1 << event, &item);
+       }
+out_unlock:
+       rcu_read_unlock();
+}
+
 int nf_conntrack_register_notifier(struct net *net,
                                   struct nf_ct_event_notifier *new)
 {
This page took 0.026038 seconds and 5 git commands to generate.