Commit | Line | Data |
---|---|---|
f6180121 MJ |
1 | /* |
2 | * connection tracking event cache. | |
3 | */ | |
4 | ||
5 | #ifndef _NF_CONNTRACK_ECACHE_H | |
6 | #define _NF_CONNTRACK_ECACHE_H | |
7 | #include <net/netfilter/nf_conntrack.h> | |
8 | ||
6058fa6b | 9 | #include <net/net_namespace.h> |
f6180121 | 10 | #include <net/netfilter/nf_conntrack_expect.h> |
a0891aa6 PNA |
11 | #include <linux/netfilter/nf_conntrack_common.h> |
12 | #include <linux/netfilter/nf_conntrack_tuple_common.h> | |
13 | #include <net/netfilter/nf_conntrack_extend.h> | |
f6180121 | 14 | |
a0891aa6 | 15 | struct nf_conntrack_ecache { |
0cebe4b4 PM |
16 | unsigned long cache; /* bitops want long */ |
17 | unsigned long missed; /* missed events */ | |
18 | u16 ctmask; /* bitmask of ct events to be delivered */ | |
19 | u16 expmask; /* bitmask of expect events to be delivered */ | |
15e47304 | 20 | u32 portid; /* netlink portid of destroyer */ |
5b423f6a | 21 | struct timer_list timeout; |
a0891aa6 | 22 | }; |
6bfea198 | 23 | |
a0891aa6 PNA |
24 | static inline struct nf_conntrack_ecache * |
25 | nf_ct_ecache_find(const struct nf_conn *ct) | |
26 | { | |
e0e76c83 | 27 | #ifdef CONFIG_NF_CONNTRACK_EVENTS |
a0891aa6 | 28 | return nf_ct_ext_find(ct, NF_CT_EXT_ECACHE); |
e0e76c83 CG |
29 | #else |
30 | return NULL; | |
31 | #endif | |
a0891aa6 | 32 | } |
6bfea198 | 33 | |
a0891aa6 | 34 | static inline struct nf_conntrack_ecache * |
0cebe4b4 | 35 | nf_ct_ecache_ext_add(struct nf_conn *ct, u16 ctmask, u16 expmask, gfp_t gfp) |
a0891aa6 | 36 | { |
e0e76c83 | 37 | #ifdef CONFIG_NF_CONNTRACK_EVENTS |
a0891aa6 | 38 | struct net *net = nf_ct_net(ct); |
0cebe4b4 | 39 | struct nf_conntrack_ecache *e; |
6bfea198 | 40 | |
0cebe4b4 PM |
41 | if (!ctmask && !expmask && net->ct.sysctl_events) { |
42 | ctmask = ~0; | |
43 | expmask = ~0; | |
44 | } | |
45 | if (!ctmask && !expmask) | |
a0891aa6 | 46 | return NULL; |
6bfea198 | 47 | |
0cebe4b4 PM |
48 | e = nf_ct_ext_add(ct, NF_CT_EXT_ECACHE, gfp); |
49 | if (e) { | |
50 | e->ctmask = ctmask; | |
51 | e->expmask = expmask; | |
52 | } | |
53 | return e; | |
e0e76c83 CG |
54 | #else |
55 | return NULL; | |
56 | #endif | |
6bfea198 PNA |
57 | }; |
58 | ||
f6180121 | 59 | #ifdef CONFIG_NF_CONNTRACK_EVENTS |
19abb7b0 PNA |
60 | /* This structure is passed to event handler */ |
61 | struct nf_ct_event { | |
62 | struct nf_conn *ct; | |
15e47304 | 63 | u32 portid; |
19abb7b0 PNA |
64 | int report; |
65 | }; | |
66 | ||
e34d5c1a PNA |
67 | struct nf_ct_event_notifier { |
68 | int (*fcn)(unsigned int events, struct nf_ct_event *item); | |
69 | }; | |
70 | ||
70e9942f PNA |
71 | extern int nf_conntrack_register_notifier(struct net *net, struct nf_ct_event_notifier *nb); |
72 | extern void nf_conntrack_unregister_notifier(struct net *net, struct nf_ct_event_notifier *nb); | |
f6180121 | 73 | |
a0891aa6 | 74 | extern void nf_ct_deliver_cached_events(struct nf_conn *ct); |
f6180121 MJ |
75 | |
76 | static inline void | |
a71996fc | 77 | nf_conntrack_event_cache(enum ip_conntrack_events event, struct nf_conn *ct) |
f6180121 | 78 | { |
70e9942f | 79 | struct net *net = nf_ct_net(ct); |
a0891aa6 PNA |
80 | struct nf_conntrack_ecache *e; |
81 | ||
6bd0405b | 82 | if (!rcu_access_pointer(net->ct.nf_conntrack_event_cb)) |
a0891aa6 PNA |
83 | return; |
84 | ||
85 | e = nf_ct_ecache_find(ct); | |
86 | if (e == NULL) | |
87 | return; | |
88 | ||
89 | set_bit(event, &e->cache); | |
f6180121 MJ |
90 | } |
91 | ||
dd7669a9 | 92 | static inline int |
a0891aa6 PNA |
93 | nf_conntrack_eventmask_report(unsigned int eventmask, |
94 | struct nf_conn *ct, | |
15e47304 | 95 | u32 portid, |
a0891aa6 | 96 | int report) |
f6180121 | 97 | { |
dd7669a9 | 98 | int ret = 0; |
70e9942f | 99 | struct net *net = nf_ct_net(ct); |
e34d5c1a | 100 | struct nf_ct_event_notifier *notify; |
dd7669a9 | 101 | struct nf_conntrack_ecache *e; |
e34d5c1a PNA |
102 | |
103 | rcu_read_lock(); | |
70e9942f | 104 | notify = rcu_dereference(net->ct.nf_conntrack_event_cb); |
e34d5c1a PNA |
105 | if (notify == NULL) |
106 | goto out_unlock; | |
107 | ||
dd7669a9 PNA |
108 | e = nf_ct_ecache_find(ct); |
109 | if (e == NULL) | |
110 | goto out_unlock; | |
111 | ||
e34d5c1a PNA |
112 | if (nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct)) { |
113 | struct nf_ct_event item = { | |
114 | .ct = ct, | |
15e47304 | 115 | .portid = e->portid ? e->portid : portid, |
e34d5c1a PNA |
116 | .report = report |
117 | }; | |
dd7669a9 | 118 | /* This is a resent of a destroy event? If so, skip missed */ |
15e47304 | 119 | unsigned long missed = e->portid ? 0 : e->missed; |
dd7669a9 | 120 | |
0cebe4b4 PM |
121 | if (!((eventmask | missed) & e->ctmask)) |
122 | goto out_unlock; | |
123 | ||
dd7669a9 PNA |
124 | ret = notify->fcn(eventmask | missed, &item); |
125 | if (unlikely(ret < 0 || missed)) { | |
126 | spin_lock_bh(&ct->lock); | |
127 | if (ret < 0) { | |
128 | /* This is a destroy event that has been | |
15e47304 | 129 | * triggered by a process, we store the PORTID |
dd7669a9 PNA |
130 | * to include it in the retransmission. */ |
131 | if (eventmask & (1 << IPCT_DESTROY) && | |
15e47304 EB |
132 | e->portid == 0 && portid != 0) |
133 | e->portid = portid; | |
dd7669a9 PNA |
134 | else |
135 | e->missed |= eventmask; | |
136 | } else | |
137 | e->missed &= ~missed; | |
138 | spin_unlock_bh(&ct->lock); | |
139 | } | |
e34d5c1a PNA |
140 | } |
141 | out_unlock: | |
142 | rcu_read_unlock(); | |
dd7669a9 | 143 | return ret; |
f6180121 MJ |
144 | } |
145 | ||
dd7669a9 | 146 | static inline int |
a0891aa6 | 147 | nf_conntrack_event_report(enum ip_conntrack_events event, struct nf_conn *ct, |
15e47304 | 148 | u32 portid, int report) |
a0891aa6 | 149 | { |
15e47304 | 150 | return nf_conntrack_eventmask_report(1 << event, ct, portid, report); |
a0891aa6 PNA |
151 | } |
152 | ||
dd7669a9 | 153 | static inline int |
19abb7b0 PNA |
154 | nf_conntrack_event(enum ip_conntrack_events event, struct nf_conn *ct) |
155 | { | |
dd7669a9 | 156 | return nf_conntrack_eventmask_report(1 << event, ct, 0, 0); |
19abb7b0 PNA |
157 | } |
158 | ||
159 | struct nf_exp_event { | |
160 | struct nf_conntrack_expect *exp; | |
15e47304 | 161 | u32 portid; |
19abb7b0 PNA |
162 | int report; |
163 | }; | |
164 | ||
e34d5c1a PNA |
165 | struct nf_exp_event_notifier { |
166 | int (*fcn)(unsigned int events, struct nf_exp_event *item); | |
167 | }; | |
168 | ||
70e9942f PNA |
169 | extern int nf_ct_expect_register_notifier(struct net *net, struct nf_exp_event_notifier *nb); |
170 | extern void nf_ct_expect_unregister_notifier(struct net *net, struct nf_exp_event_notifier *nb); | |
010c7d6f | 171 | |
19abb7b0 PNA |
172 | static inline void |
173 | nf_ct_expect_event_report(enum ip_conntrack_expect_events event, | |
174 | struct nf_conntrack_expect *exp, | |
15e47304 | 175 | u32 portid, |
19abb7b0 PNA |
176 | int report) |
177 | { | |
70e9942f | 178 | struct net *net = nf_ct_exp_net(exp); |
e34d5c1a | 179 | struct nf_exp_event_notifier *notify; |
0cebe4b4 | 180 | struct nf_conntrack_ecache *e; |
e34d5c1a PNA |
181 | |
182 | rcu_read_lock(); | |
70e9942f | 183 | notify = rcu_dereference(net->ct.nf_expect_event_cb); |
e34d5c1a PNA |
184 | if (notify == NULL) |
185 | goto out_unlock; | |
186 | ||
0cebe4b4 PM |
187 | e = nf_ct_ecache_find(exp->master); |
188 | if (e == NULL) | |
a0891aa6 PNA |
189 | goto out_unlock; |
190 | ||
0cebe4b4 | 191 | if (e->expmask & (1 << event)) { |
e34d5c1a PNA |
192 | struct nf_exp_event item = { |
193 | .exp = exp, | |
15e47304 | 194 | .portid = portid, |
e34d5c1a PNA |
195 | .report = report |
196 | }; | |
a0891aa6 | 197 | notify->fcn(1 << event, &item); |
e34d5c1a PNA |
198 | } |
199 | out_unlock: | |
200 | rcu_read_unlock(); | |
19abb7b0 PNA |
201 | } |
202 | ||
f6180121 | 203 | static inline void |
6823645d PM |
204 | nf_ct_expect_event(enum ip_conntrack_expect_events event, |
205 | struct nf_conntrack_expect *exp) | |
f6180121 | 206 | { |
19abb7b0 | 207 | nf_ct_expect_event_report(event, exp, 0, 0); |
f6180121 MJ |
208 | } |
209 | ||
6058fa6b AD |
210 | extern int nf_conntrack_ecache_init(struct net *net); |
211 | extern void nf_conntrack_ecache_fini(struct net *net); | |
212 | ||
f6180121 MJ |
213 | #else /* CONFIG_NF_CONNTRACK_EVENTS */ |
214 | ||
215 | static inline void nf_conntrack_event_cache(enum ip_conntrack_events event, | |
64f1b653 | 216 | struct nf_conn *ct) {} |
dd7669a9 PNA |
217 | static inline int nf_conntrack_eventmask_report(unsigned int eventmask, |
218 | struct nf_conn *ct, | |
15e47304 | 219 | u32 portid, |
dd7669a9 PNA |
220 | int report) { return 0; } |
221 | static inline int nf_conntrack_event(enum ip_conntrack_events event, | |
222 | struct nf_conn *ct) { return 0; } | |
223 | static inline int nf_conntrack_event_report(enum ip_conntrack_events event, | |
224 | struct nf_conn *ct, | |
15e47304 | 225 | u32 portid, |
dd7669a9 | 226 | int report) { return 0; } |
f6180121 | 227 | static inline void nf_ct_deliver_cached_events(const struct nf_conn *ct) {} |
6823645d PM |
228 | static inline void nf_ct_expect_event(enum ip_conntrack_expect_events event, |
229 | struct nf_conntrack_expect *exp) {} | |
19abb7b0 PNA |
230 | static inline void nf_ct_expect_event_report(enum ip_conntrack_expect_events e, |
231 | struct nf_conntrack_expect *exp, | |
15e47304 | 232 | u32 portid, |
19abb7b0 | 233 | int report) {} |
6058fa6b AD |
234 | |
235 | static inline int nf_conntrack_ecache_init(struct net *net) | |
236 | { | |
237 | return 0; | |
bb21c95e | 238 | } |
6058fa6b AD |
239 | |
240 | static inline void nf_conntrack_ecache_fini(struct net *net) | |
241 | { | |
242 | } | |
f6180121 MJ |
243 | #endif /* CONFIG_NF_CONNTRACK_EVENTS */ |
244 | ||
245 | #endif /*_NF_CONNTRACK_ECACHE_H*/ | |
246 |