2 * IP_MASQ_APP application masquerading module
5 * $Id: ip_masq_app.c,v 1.16 1998/08/29 23:51:14 davem Exp $
7 * Author: Juan Jose Ciarlante, <jjciarla@raiz.uncu.edu.ar>
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version
13 * 2 of the License, or (at your option) any later version.
16 * JJC : Implemented also input pkt hook
17 * Miquel van Smoorenburg : Copy more stuff when resizing skb
21 * - ip_masq_skb_replace(): use same skb if space available.
25 #include <linux/config.h>
26 #include <linux/module.h>
27 #include <linux/types.h>
28 #include <linux/kernel.h>
29 #include <linux/errno.h>
30 #include <linux/skbuff.h>
33 #include <linux/init.h>
34 #include <net/protocol.h>
37 #include <asm/system.h>
38 #include <linux/stat.h>
39 #include <linux/proc_fs.h>
40 #include <net/ip_masq.h>
42 #define IP_MASQ_APP_TAB_SIZE 16 /* must be power of 2 */
44 #define IP_MASQ_APP_HASH(proto, port) ((port^proto) & (IP_MASQ_APP_TAB_SIZE-1))
45 #define IP_MASQ_APP_TYPE(proto, port) ( proto<<16 | port )
46 #define IP_MASQ_APP_PORT(type) ( type & 0xffff )
47 #define IP_MASQ_APP_PROTO(type) ( (type>>16) & 0x00ff )
50 EXPORT_SYMBOL(register_ip_masq_app
);
51 EXPORT_SYMBOL(unregister_ip_masq_app
);
52 EXPORT_SYMBOL(ip_masq_skb_replace
);
55 * will hold masq app. hashed list heads
58 struct ip_masq_app
*ip_masq_app_base
[IP_MASQ_APP_TAB_SIZE
];
61 * ip_masq_app registration routine
62 * port: host byte order.
65 int register_ip_masq_app(struct ip_masq_app
*mapp
, unsigned short proto
, __u16 port
)
70 IP_MASQ_ERR("register_ip_masq_app(): NULL arg\n");
73 mapp
->type
= IP_MASQ_APP_TYPE(proto
, port
);
75 hash
= IP_MASQ_APP_HASH(proto
, port
);
79 mapp
->next
= ip_masq_app_base
[hash
];
80 ip_masq_app_base
[hash
] = mapp
;
87 * ip_masq_app unreg. routine.
90 int unregister_ip_masq_app(struct ip_masq_app
*mapp
)
92 struct ip_masq_app
**mapp_p
;
96 IP_MASQ_ERR("unregister_ip_masq_app(): NULL arg\n");
100 * only allow unregistration if it has no attachments
102 if (mapp
->n_attach
) {
103 IP_MASQ_ERR("unregister_ip_masq_app(): has %d attachments. failed\n",
107 hash
= IP_MASQ_APP_HASH(IP_MASQ_APP_PROTO(mapp
->type
), IP_MASQ_APP_PORT(mapp
->type
));
111 for (mapp_p
= &ip_masq_app_base
[hash
]; *mapp_p
; mapp_p
= &(*mapp_p
)->next
)
112 if (mapp
== (*mapp_p
)) {
113 *mapp_p
= mapp
->next
;
114 restore_flags(flags
);
118 restore_flags(flags
);
119 IP_MASQ_ERR("unregister_ip_masq_app(proto=%s,port=%u): not hashed!\n",
120 masq_proto_name(IP_MASQ_APP_PROTO(mapp
->type
)), IP_MASQ_APP_PORT(mapp
->type
));
125 * get ip_masq_app object by its proto and port (net byte order).
128 struct ip_masq_app
* ip_masq_app_get(unsigned short proto
, __u16 port
)
130 struct ip_masq_app
*mapp
;
135 type
= IP_MASQ_APP_TYPE(proto
,port
);
136 hash
= IP_MASQ_APP_HASH(proto
,port
);
137 for(mapp
= ip_masq_app_base
[hash
]; mapp
; mapp
= mapp
->next
) {
138 if (type
== mapp
->type
) return mapp
;
144 * ip_masq_app object binding related funcs.
148 * change ip_masq_app object's number of bindings
151 static __inline__
int ip_masq_app_bind_chg(struct ip_masq_app
*mapp
, int delta
)
155 if (!mapp
) return -1;
158 n_at
= mapp
->n_attach
+ delta
;
160 restore_flags(flags
);
161 IP_MASQ_ERR("ip_masq_app: tried to set n_attach < 0 for (proto=%s,port==%d) ip_masq_app object.\n",
162 masq_proto_name(IP_MASQ_APP_PROTO(mapp
->type
)),
163 IP_MASQ_APP_PORT(mapp
->type
));
166 mapp
->n_attach
= n_at
;
167 restore_flags(flags
);
172 * Bind ip_masq to its ip_masq_app based on proto and dport ALREADY
173 * set in ip_masq struct. Also calls constructor.
176 struct ip_masq_app
* ip_masq_bind_app(struct ip_masq
*ms
)
178 struct ip_masq_app
* mapp
;
180 if (ms
->protocol
!= IPPROTO_TCP
&& ms
->protocol
!= IPPROTO_UDP
)
183 mapp
= ip_masq_app_get(ms
->protocol
, ms
->dport
);
186 /* #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW */
188 mapp
= ip_masq_app_get(ms
->protocol
, ms
->sport
);
194 * don't allow binding if already bound
197 if (ms
->app
!= NULL
) {
198 IP_MASQ_ERR("ip_masq_bind_app() called for already bound object.\n");
203 if (mapp
->masq_init_1
) mapp
->masq_init_1(mapp
, ms
);
204 ip_masq_app_bind_chg(mapp
, +1);
210 * Unbind ms from type object and call ms destructor (does not kfree()).
213 int ip_masq_unbind_app(struct ip_masq
*ms
)
215 struct ip_masq_app
* mapp
;
218 if (ms
->protocol
!= IPPROTO_TCP
&& ms
->protocol
!= IPPROTO_UDP
)
222 if (mapp
->masq_done_1
) mapp
->masq_done_1(mapp
, ms
);
224 ip_masq_app_bind_chg(mapp
, -1);
226 return (mapp
!= NULL
);
230 * Fixes th->seq based on ip_masq_seq info.
233 static __inline__
void masq_fix_seq(const struct ip_masq_seq
*ms_seq
, struct tcphdr
*th
)
237 seq
= ntohl(th
->seq
);
240 * Adjust seq with delta-offset for all packets after
241 * the most recent resized pkt seq and with previous_delta offset
242 * for all packets before most recent resized pkt seq.
245 if (ms_seq
->delta
|| ms_seq
->previous_delta
) {
246 if(after(seq
,ms_seq
->init_seq
) ) {
247 th
->seq
= htonl(seq
+ ms_seq
->delta
);
248 IP_MASQ_DEBUG(1, "masq_fix_seq() : added delta (%d) to seq\n",ms_seq
->delta
);
250 th
->seq
= htonl(seq
+ ms_seq
->previous_delta
);
251 IP_MASQ_DEBUG(1, "masq_fix_seq() : added previous_delta (%d) to seq\n",ms_seq
->previous_delta
);
259 * Fixes th->ack_seq based on ip_masq_seq info.
262 static __inline__
void masq_fix_ack_seq(const struct ip_masq_seq
*ms_seq
, struct tcphdr
*th
)
266 ack_seq
=ntohl(th
->ack_seq
);
269 * Adjust ack_seq with delta-offset for
270 * the packets AFTER most recent resized pkt has caused a shift
271 * for packets before most recent resized pkt, use previous_delta
274 if (ms_seq
->delta
|| ms_seq
->previous_delta
) {
275 if(after(ack_seq
,ms_seq
->init_seq
)) {
276 th
->ack_seq
= htonl(ack_seq
-ms_seq
->delta
);
277 IP_MASQ_DEBUG(1, "masq_fix_ack_seq() : subtracted delta (%d) from ack_seq\n",ms_seq
->delta
);
280 th
->ack_seq
= htonl(ack_seq
-ms_seq
->previous_delta
);
281 IP_MASQ_DEBUG(1, "masq_fix_ack_seq() : subtracted previous_delta (%d) from ack_seq\n",ms_seq
->previous_delta
);
288 * Updates ip_masq_seq if pkt has been resized
289 * Assumes already checked proto==IPPROTO_TCP and diff!=0.
292 static __inline__
void masq_seq_update(struct ip_masq
*ms
, struct ip_masq_seq
*ms_seq
, unsigned mflag
, __u32 seq
, int diff
)
294 /* if (diff == 0) return; */
296 if ( !(ms
->flags
& mflag
) || after(seq
, ms_seq
->init_seq
))
298 ms_seq
->previous_delta
=ms_seq
->delta
;
300 ms_seq
->init_seq
=seq
;
306 * Output pkt hook. Will call bound ip_masq_app specific function
307 * called by ip_fw_masquerade(), assumes previously checked ms!=NULL
308 * returns (new - old) skb->len diff.
311 int ip_masq_app_pkt_out(struct ip_masq
*ms
, struct sk_buff
**skb_p
, __u32 maddr
)
313 struct ip_masq_app
* mapp
;
320 * check if application masquerading is bound to
322 * assumes that once an ip_masq is bound,
323 * it will not be unbound during its life.
326 if ( (mapp
= ms
->app
) == NULL
)
329 iph
= (*skb_p
)->nh
.iph
;
330 th
= (struct tcphdr
*)&(((char *)iph
)[iph
->ihl
*4]);
333 * Remember seq number in case this pkt gets resized
336 seq
= ntohl(th
->seq
);
339 * Fix seq stuff if flagged as so.
342 if (ms
->protocol
== IPPROTO_TCP
) {
343 if (ms
->flags
& IP_MASQ_F_OUT_SEQ
)
344 masq_fix_seq(&ms
->out_seq
, th
);
345 if (ms
->flags
& IP_MASQ_F_IN_SEQ
)
346 masq_fix_ack_seq(&ms
->in_seq
, th
);
350 * Call private output hook function
353 if ( mapp
->pkt_out
== NULL
)
356 diff
= mapp
->pkt_out(mapp
, ms
, skb_p
, maddr
);
359 * Update ip_masq seq stuff if len has changed.
362 if (diff
!= 0 && ms
->protocol
== IPPROTO_TCP
)
363 masq_seq_update(ms
, &ms
->out_seq
, IP_MASQ_F_OUT_SEQ
, seq
, diff
);
369 * Input pkt hook. Will call bound ip_masq_app specific function
370 * called by ip_fw_demasquerade(), assumes previously checked ms!=NULL.
371 * returns (new - old) skb->len diff.
374 int ip_masq_app_pkt_in(struct ip_masq
*ms
, struct sk_buff
**skb_p
, __u32 maddr
)
376 struct ip_masq_app
* mapp
;
383 * check if application masquerading is bound to
385 * assumes that once an ip_masq is bound,
386 * it will not be unbound during its life.
389 if ( (mapp
= ms
->app
) == NULL
)
392 iph
= (*skb_p
)->nh
.iph
;
393 th
= (struct tcphdr
*)&(((char *)iph
)[iph
->ihl
*4]);
396 * Remember seq number in case this pkt gets resized
399 seq
= ntohl(th
->seq
);
402 * Fix seq stuff if flagged as so.
405 if (ms
->protocol
== IPPROTO_TCP
) {
406 if (ms
->flags
& IP_MASQ_F_IN_SEQ
)
407 masq_fix_seq(&ms
->in_seq
, th
);
408 if (ms
->flags
& IP_MASQ_F_OUT_SEQ
)
409 masq_fix_ack_seq(&ms
->out_seq
, th
);
413 * Call private input hook function
416 if ( mapp
->pkt_in
== NULL
)
419 diff
= mapp
->pkt_in(mapp
, ms
, skb_p
, maddr
);
422 * Update ip_masq seq stuff if len has changed.
425 if (diff
!= 0 && ms
->protocol
== IPPROTO_TCP
)
426 masq_seq_update(ms
, &ms
->in_seq
, IP_MASQ_F_IN_SEQ
, seq
, diff
);
432 * /proc/ip_masq_app entry function
435 int ip_masq_app_getinfo(char *buffer
, char **start
, off_t offset
, int length
, int dummy
)
437 off_t pos
=0, begin
=0;
439 struct ip_masq_app
* mapp
;
443 len
=sprintf(buffer
,"%-39s\n", "prot port n_attach name");
446 for (idx
=0 ; idx
< IP_MASQ_APP_TAB_SIZE
; idx
++)
447 for (mapp
= ip_masq_app_base
[idx
]; mapp
; mapp
= mapp
->next
) {
449 * If you change the length of this sprintf, then all
450 * the length calculations need fixing too!
451 * Line length = 40 (3 + 2 + 7 + 1 + 7 + 1 + 2 + 17)
457 len
+= sprintf(buffer
+len
, "%-3s %-7u %-7d %-17s\n",
458 masq_proto_name(IP_MASQ_APP_PROTO(mapp
->type
)),
459 IP_MASQ_APP_PORT(mapp
->type
), mapp
->n_attach
,
466 begin
= len
- (pos
- offset
);
467 *start
= buffer
+ begin
;
475 #ifdef CONFIG_PROC_FS
476 static struct proc_dir_entry proc_net_ip_masq_app
= {
477 PROC_NET_IP_MASQ_APP
, 3, "app",
478 S_IFREG
| S_IRUGO
, 1, 0, 0,
479 0, &proc_net_inode_operations
,
485 * Initialization routine
488 __initfunc(int ip_masq_app_init(void))
490 #ifdef CONFIG_PROC_FS
491 ip_masq_proc_register(&proc_net_ip_masq_app
);
497 * Replace a segment (of skb->data) with a new one.
498 * FIXME: Should re-use same skb if space available, this could
499 * be done if n_len < o_len, unless some extra space
500 * were already allocated at driver level :P .
503 static struct sk_buff
* skb_replace(struct sk_buff
*skb
, int pri
, char *o_buf
, int o_len
, char *n_buf
, int n_len
)
505 int maxsize
, diff
, o_offset
;
506 struct sk_buff
*n_skb
;
509 maxsize
= skb
->truesize
;
511 diff
= n_len
- o_len
;
512 o_offset
= o_buf
- (char*) skb
->data
;
514 if (maxsize
<= n_len
) {
516 memcpy(skb
->data
+ o_offset
+ n_len
,o_buf
+ o_len
,
517 skb
->len
- (o_offset
+ o_len
));
520 memcpy(skb
->data
+ o_offset
, n_buf
, n_len
);
524 skb
->end
= skb
->head
+n_len
;
527 * Sizes differ, make a copy.
529 * FIXME: move this to core/sbuff.c:skb_grow()
532 n_skb
= alloc_skb(MAX_HEADER
+ skb
->len
+ diff
, pri
);
534 IP_MASQ_ERR("skb_replace(): no room left (from %p)\n",
535 __builtin_return_address(0));
539 skb_reserve(n_skb
, MAX_HEADER
);
540 skb_put(n_skb
, skb
->len
+ diff
);
543 * Copy as much data from the old skb as possible. Even
544 * though we're only forwarding packets, we need stuff
545 * like skb->protocol (PPP driver wants it).
547 offset
= n_skb
->data
- skb
->data
;
548 n_skb
->nh
.raw
= skb
->nh
.raw
+ offset
;
549 n_skb
->h
.raw
= skb
->h
.raw
+ offset
;
550 n_skb
->dev
= skb
->dev
;
551 n_skb
->mac
.raw
= skb
->mac
.raw
+ offset
;
552 n_skb
->pkt_type
= skb
->pkt_type
;
553 n_skb
->protocol
= skb
->protocol
;
554 n_skb
->ip_summed
= skb
->ip_summed
;
555 n_skb
->dst
= dst_clone(skb
->dst
);
558 * Copy pkt in new buffer
561 memcpy(n_skb
->data
, skb
->data
, o_offset
);
562 memcpy(n_skb
->data
+ o_offset
, n_buf
, n_len
);
563 memcpy(n_skb
->data
+ o_offset
+ n_len
, o_buf
+ o_len
,
564 skb
->len
- (o_offset
+ o_len
) );
567 * Problem, how to replace the new skb with old one,
577 * calls skb_replace() and update ip header if new skb was allocated
580 struct sk_buff
* ip_masq_skb_replace(struct sk_buff
*skb
, int pri
, char *o_buf
, int o_len
, char *n_buf
, int n_len
)
583 struct sk_buff
*n_skb
;
586 diff
= n_len
- o_len
;
587 n_skb
= skb_replace(skb
, pri
, o_buf
, o_len
, n_buf
, n_len
);
593 IP_MASQ_DEBUG(1, "masq_skb_replace(): pkt resized for %d bytes (len=%d)\n", diff
, skb
->len
);
599 iph
->check
= ip_fast_csum((unsigned char *)iph
, iph
->ihl
);
600 iph
->tot_len
= htons(skb_len
+ diff
);