Commit | Line | Data |
---|---|---|
f6ebe77f | 1 | #include <linux/kernel.h> |
5a0e3ad6 | 2 | #include <linux/slab.h> |
f6ebe77f HW |
3 | #include <linux/init.h> |
4 | #include <linux/module.h> | |
5 | #include <linux/proc_fs.h> | |
6 | #include <linux/skbuff.h> | |
7 | #include <linux/netfilter.h> | |
bbd86b9f | 8 | #include <linux/seq_file.h> |
7a11b984 | 9 | #include <linux/rcupdate.h> |
f6ebe77f | 10 | #include <net/protocol.h> |
c01cd429 | 11 | #include <net/netfilter/nf_queue.h> |
7fee226a | 12 | #include <net/dst.h> |
f6ebe77f HW |
13 | |
14 | #include "nf_internals.h" | |
15 | ||
601e68e1 | 16 | /* |
0360ae41 FW |
17 | * Hook for nfnetlink_queue to register its queue handler. |
18 | * We do this so that most of the NFQUEUE code can be modular. | |
19 | * | |
20 | * Once the queue is registered it must reinject all packets it | |
21 | * receives, no matter what. | |
f6ebe77f | 22 | */ |
0360ae41 | 23 | static const struct nf_queue_handler __rcu *queue_handler __read_mostly; |
f6ebe77f | 24 | |
d72367b6 HW |
25 | /* return EBUSY when somebody else is registered, return EEXIST if the |
26 | * same handler is registered, return 0 in case of success. */ | |
0360ae41 | 27 | void nf_register_queue_handler(const struct nf_queue_handler *qh) |
601e68e1 | 28 | { |
0360ae41 FW |
29 | /* should never happen, we only have one queueing backend in kernel */ |
30 | WARN_ON(rcu_access_pointer(queue_handler)); | |
31 | rcu_assign_pointer(queue_handler, qh); | |
f6ebe77f HW |
32 | } |
33 | EXPORT_SYMBOL(nf_register_queue_handler); | |
34 | ||
35 | /* The caller must flush their queue before this */ | |
0360ae41 | 36 | void nf_unregister_queue_handler(void) |
f6ebe77f | 37 | { |
0360ae41 | 38 | RCU_INIT_POINTER(queue_handler, NULL); |
585426fd | 39 | synchronize_rcu(); |
f6ebe77f HW |
40 | } |
41 | EXPORT_SYMBOL(nf_unregister_queue_handler); | |
42 | ||
daaa8be2 PM |
43 | static void nf_queue_entry_release_refs(struct nf_queue_entry *entry) |
44 | { | |
45 | /* Release those devices we held, or Alexey will kill me. */ | |
46 | if (entry->indev) | |
47 | dev_put(entry->indev); | |
48 | if (entry->outdev) | |
49 | dev_put(entry->outdev); | |
50 | #ifdef CONFIG_BRIDGE_NETFILTER | |
51 | if (entry->skb->nf_bridge) { | |
52 | struct nf_bridge_info *nf_bridge = entry->skb->nf_bridge; | |
53 | ||
54 | if (nf_bridge->physindev) | |
55 | dev_put(nf_bridge->physindev); | |
56 | if (nf_bridge->physoutdev) | |
57 | dev_put(nf_bridge->physoutdev); | |
58 | } | |
59 | #endif | |
60 | /* Drop reference to owner of hook which queued us. */ | |
61 | module_put(entry->elem->owner); | |
62 | } | |
63 | ||
601e68e1 YH |
64 | /* |
65 | * Any packet that leaves via this function must come back | |
f6ebe77f HW |
66 | * through nf_reinject(). |
67 | */ | |
394f545d | 68 | static int __nf_queue(struct sk_buff *skb, |
1c15b677 | 69 | struct nf_hook_ops *elem, |
76108cea | 70 | u_int8_t pf, unsigned int hook, |
394f545d PM |
71 | struct net_device *indev, |
72 | struct net_device *outdev, | |
73 | int (*okfn)(struct sk_buff *), | |
74 | unsigned int queuenum) | |
f6ebe77f | 75 | { |
f1585086 | 76 | int status = -ENOENT; |
daaa8be2 | 77 | struct nf_queue_entry *entry = NULL; |
f6ebe77f | 78 | #ifdef CONFIG_BRIDGE_NETFILTER |
daaa8be2 PM |
79 | struct net_device *physindev; |
80 | struct net_device *physoutdev; | |
f6ebe77f | 81 | #endif |
1e796fda | 82 | const struct nf_afinfo *afinfo; |
e3ac5298 | 83 | const struct nf_queue_handler *qh; |
f6ebe77f | 84 | |
25985edc | 85 | /* QUEUE == DROP if no one is waiting, to be safe. */ |
585426fd YK |
86 | rcu_read_lock(); |
87 | ||
0360ae41 | 88 | qh = rcu_dereference(queue_handler); |
94b27cc3 FW |
89 | if (!qh) { |
90 | status = -ESRCH; | |
daaa8be2 | 91 | goto err_unlock; |
94b27cc3 | 92 | } |
f6ebe77f | 93 | |
bce8032e | 94 | afinfo = nf_get_afinfo(pf); |
daaa8be2 PM |
95 | if (!afinfo) |
96 | goto err_unlock; | |
bce8032e | 97 | |
02f014d8 | 98 | entry = kmalloc(sizeof(*entry) + afinfo->route_key_size, GFP_ATOMIC); |
f1585086 FW |
99 | if (!entry) { |
100 | status = -ENOMEM; | |
daaa8be2 | 101 | goto err_unlock; |
f1585086 | 102 | } |
f6ebe77f | 103 | |
02f014d8 PM |
104 | *entry = (struct nf_queue_entry) { |
105 | .skb = skb, | |
1c15b677 | 106 | .elem = elem, |
02f014d8 PM |
107 | .pf = pf, |
108 | .hook = hook, | |
109 | .indev = indev, | |
110 | .outdev = outdev, | |
111 | .okfn = okfn, | |
112 | }; | |
f6ebe77f HW |
113 | |
114 | /* If it's going away, ignore hook. */ | |
02f014d8 | 115 | if (!try_module_get(entry->elem->owner)) { |
06cdb634 FW |
116 | status = -ECANCELED; |
117 | goto err_unlock; | |
f6ebe77f | 118 | } |
f6ebe77f | 119 | /* Bump dev refs so they don't vanish while packet is out */ |
8b1cf0db PM |
120 | if (indev) |
121 | dev_hold(indev); | |
122 | if (outdev) | |
123 | dev_hold(outdev); | |
f6ebe77f | 124 | #ifdef CONFIG_BRIDGE_NETFILTER |
394f545d PM |
125 | if (skb->nf_bridge) { |
126 | physindev = skb->nf_bridge->physindev; | |
8b1cf0db PM |
127 | if (physindev) |
128 | dev_hold(physindev); | |
394f545d | 129 | physoutdev = skb->nf_bridge->physoutdev; |
8b1cf0db PM |
130 | if (physoutdev) |
131 | dev_hold(physoutdev); | |
f6ebe77f HW |
132 | } |
133 | #endif | |
7fee226a | 134 | skb_dst_force(skb); |
02f014d8 PM |
135 | afinfo->saveroute(skb, entry); |
136 | status = qh->outfn(entry, queuenum); | |
f6ebe77f | 137 | |
585426fd | 138 | rcu_read_unlock(); |
f6ebe77f HW |
139 | |
140 | if (status < 0) { | |
daaa8be2 PM |
141 | nf_queue_entry_release_refs(entry); |
142 | goto err; | |
f6ebe77f HW |
143 | } |
144 | ||
f1585086 | 145 | return 0; |
daaa8be2 PM |
146 | |
147 | err_unlock: | |
148 | rcu_read_unlock(); | |
149 | err: | |
daaa8be2 | 150 | kfree(entry); |
f1585086 | 151 | return status; |
f6ebe77f HW |
152 | } |
153 | ||
a8db7b2d FW |
154 | #ifdef CONFIG_BRIDGE_NETFILTER |
155 | /* When called from bridge netfilter, skb->data must point to MAC header | |
156 | * before calling skb_gso_segment(). Else, original MAC header is lost | |
157 | * and segmented skbs will be sent to wrong destination. | |
158 | */ | |
159 | static void nf_bridge_adjust_skb_data(struct sk_buff *skb) | |
160 | { | |
161 | if (skb->nf_bridge) | |
162 | __skb_push(skb, skb->network_header - skb->mac_header); | |
163 | } | |
164 | ||
165 | static void nf_bridge_adjust_segmented_data(struct sk_buff *skb) | |
166 | { | |
167 | if (skb->nf_bridge) | |
168 | __skb_pull(skb, skb->network_header - skb->mac_header); | |
169 | } | |
170 | #else | |
171 | #define nf_bridge_adjust_skb_data(s) do {} while (0) | |
172 | #define nf_bridge_adjust_segmented_data(s) do {} while (0) | |
173 | #endif | |
174 | ||
394f545d | 175 | int nf_queue(struct sk_buff *skb, |
1c15b677 | 176 | struct nf_hook_ops *elem, |
76108cea | 177 | u_int8_t pf, unsigned int hook, |
394f545d PM |
178 | struct net_device *indev, |
179 | struct net_device *outdev, | |
180 | int (*okfn)(struct sk_buff *), | |
181 | unsigned int queuenum) | |
182 | { | |
183 | struct sk_buff *segs; | |
a8db7b2d | 184 | int err = -EINVAL; |
f1585086 | 185 | unsigned int queued; |
394f545d PM |
186 | |
187 | if (!skb_is_gso(skb)) | |
188 | return __nf_queue(skb, elem, pf, hook, indev, outdev, okfn, | |
189 | queuenum); | |
190 | ||
191 | switch (pf) { | |
4b1e27e9 | 192 | case NFPROTO_IPV4: |
394f545d PM |
193 | skb->protocol = htons(ETH_P_IP); |
194 | break; | |
4b1e27e9 | 195 | case NFPROTO_IPV6: |
394f545d PM |
196 | skb->protocol = htons(ETH_P_IPV6); |
197 | break; | |
198 | } | |
199 | ||
a8db7b2d | 200 | nf_bridge_adjust_skb_data(skb); |
394f545d | 201 | segs = skb_gso_segment(skb, 0); |
f1585086 FW |
202 | /* Does not use PTR_ERR to limit the number of error codes that can be |
203 | * returned by nf_queue. For instance, callers rely on -ECANCELED to mean | |
204 | * 'ignore this hook'. | |
205 | */ | |
801678c5 | 206 | if (IS_ERR(segs)) |
a8db7b2d | 207 | goto out_err; |
f1585086 FW |
208 | queued = 0; |
209 | err = 0; | |
394f545d PM |
210 | do { |
211 | struct sk_buff *nskb = segs->next; | |
212 | ||
213 | segs->next = NULL; | |
a8db7b2d FW |
214 | if (err == 0) { |
215 | nf_bridge_adjust_segmented_data(segs); | |
f1585086 FW |
216 | err = __nf_queue(segs, elem, pf, hook, indev, |
217 | outdev, okfn, queuenum); | |
a8db7b2d | 218 | } |
f1585086 FW |
219 | if (err == 0) |
220 | queued++; | |
221 | else | |
394f545d PM |
222 | kfree_skb(segs); |
223 | segs = nskb; | |
224 | } while (segs); | |
f1585086 | 225 | |
a8db7b2d | 226 | if (queued) { |
06cdb634 | 227 | kfree_skb(skb); |
a8db7b2d FW |
228 | return 0; |
229 | } | |
230 | out_err: | |
231 | nf_bridge_adjust_segmented_data(skb); | |
f1585086 | 232 | return err; |
394f545d PM |
233 | } |
234 | ||
02f014d8 | 235 | void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict) |
f6ebe77f | 236 | { |
02f014d8 | 237 | struct sk_buff *skb = entry->skb; |
2a6decfd | 238 | struct nf_hook_ops *elem = entry->elem; |
1e796fda | 239 | const struct nf_afinfo *afinfo; |
f1585086 | 240 | int err; |
f6ebe77f HW |
241 | |
242 | rcu_read_lock(); | |
243 | ||
daaa8be2 | 244 | nf_queue_entry_release_refs(entry); |
f6ebe77f | 245 | |
f6ebe77f HW |
246 | /* Continue traversal iff userspace said ok... */ |
247 | if (verdict == NF_REPEAT) { | |
2a6decfd | 248 | elem = list_entry(elem->list.prev, struct nf_hook_ops, list); |
f6ebe77f HW |
249 | verdict = NF_ACCEPT; |
250 | } | |
251 | ||
7a11b984 | 252 | if (verdict == NF_ACCEPT) { |
02f014d8 PM |
253 | afinfo = nf_get_afinfo(entry->pf); |
254 | if (!afinfo || afinfo->reroute(skb, entry) < 0) | |
7a11b984 PM |
255 | verdict = NF_DROP; |
256 | } | |
257 | ||
f6ebe77f HW |
258 | if (verdict == NF_ACCEPT) { |
259 | next_hook: | |
02f014d8 PM |
260 | verdict = nf_iterate(&nf_hooks[entry->pf][entry->hook], |
261 | skb, entry->hook, | |
262 | entry->indev, entry->outdev, &elem, | |
263 | entry->okfn, INT_MIN); | |
f6ebe77f HW |
264 | } |
265 | ||
266 | switch (verdict & NF_VERDICT_MASK) { | |
267 | case NF_ACCEPT: | |
3bc38712 | 268 | case NF_STOP: |
4b3d15ef | 269 | local_bh_disable(); |
02f014d8 | 270 | entry->okfn(skb); |
4b3d15ef | 271 | local_bh_enable(); |
f6ebe77f | 272 | break; |
f6ebe77f | 273 | case NF_QUEUE: |
1c15b677 | 274 | err = __nf_queue(skb, elem, entry->pf, entry->hook, |
f1585086 | 275 | entry->indev, entry->outdev, entry->okfn, |
f615df76 | 276 | verdict >> NF_VERDICT_QBITS); |
06cdb634 FW |
277 | if (err < 0) { |
278 | if (err == -ECANCELED) | |
279 | goto next_hook; | |
94b27cc3 FW |
280 | if (err == -ESRCH && |
281 | (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS)) | |
282 | goto next_hook; | |
06cdb634 FW |
283 | kfree_skb(skb); |
284 | } | |
f6ebe77f | 285 | break; |
64507fdb | 286 | case NF_STOLEN: |
fad54440 | 287 | break; |
3bc38712 PM |
288 | default: |
289 | kfree_skb(skb); | |
f6ebe77f HW |
290 | } |
291 | rcu_read_unlock(); | |
02f014d8 | 292 | kfree(entry); |
f6ebe77f HW |
293 | } |
294 | EXPORT_SYMBOL(nf_reinject); |