aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/interceptor/linux_internal.h
blob: ebd0c9f40820f5b7ed139e2a41cd6be72e303fd8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
/* Netfilter Driver for IPSec VPN Client
 *
 * Copyright(c)   2012 Samsung Electronics
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

/*
 * linux_internal.h
 *
 * Internal header file for the linux interceptor.
 *
 */

#ifndef LINUX_INTERNAL_H
#define LINUX_INTERNAL_H

#include "sshincludes.h"

/* Parameters used to tune the interceptor. */
#include "linux_versions.h"
#include "linux_params.h"

#include <linux/types.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/proc_fs.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <net/pkt_sched.h>

#include <linux/interrupt.h>
#include <linux/inetdevice.h>

#include <net/ip.h>
#include <net/inet_common.h>

#ifdef SSH_LINUX_INTERCEPTOR_IPV6
#include <net/ipv6.h>
#include <net/addrconf.h>
#endif /* SSH_LINUX_INTERCEPTOR_IPV6 */

#ifndef SSH_IPSEC_IP_ONLY_INTERCEPTOR
#include <linux/if_arp.h>
#endif /* SSH_IPSEC_IP_ONLY_INTERCEPTOR */

#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
#include <linux/netfilter_bridge.h>
#include <linux/netfilter_arp.h>

#include <linux/module.h>
#include <linux/moduleparam.h>

#include <linux/cpumask.h>
#include <linux/rcupdate.h>

#ifdef LINUX_NEED_IF_ADDR_H
#include <linux/if_addr.h>
#endif /* LINUX_NEED_IF_ADDR_H */

#include <net/ip.h>
#include <net/route.h>
#include <net/inet_common.h>

#ifdef SSH_LINUX_INTERCEPTOR_IPV6
#include <net/ipv6.h>
#include <net/addrconf.h>
#include <net/ip6_fib.h>
#include <net/ip6_route.h>
#include <net/flow.h>
#endif /* SSH_LINUX_INTERCEPTOR_IPV6 */

#include <linux/threads.h>

#include "engine.h"
#include "kernel_includes.h"
#include "kernel_mutex.h"
#include "interceptor.h"
#include "sshinet.h"
#include "sshdebug.h"

#include "linux_packet_internal.h"
#include "linux_mutex_internal.h"
#include "linux_virtual_adapter_internal.h"

/****************************** Sanity checks ********************************/

#ifndef MODULE
#error "VPNClient can only be compiled as a MODULE"
#endif /* MODULE */

#ifndef CONFIG_NETFILTER
#error "Kernel is not compiled with CONFIG_NETFILTER"
#endif /* CONFIG_NETFILTER */

/* Check that SSH_LINUX_FWMARK_EXTENSION_SELECTOR is in range. */
#ifdef SSH_LINUX_FWMARK_EXTENSION_SELECTOR
#if (SSH_INTERCEPTOR_NUM_EXTENSION_SELECTORS > 0)
#if (SSH_LINUX_FWMARK_EXTENSION_SELECTOR >= \
     SSH_INTERCEPTOR_NUM_EXTENSION_SELECTORS)
#error "Invalid value specified for SSH_LINUX_FWMARK_EXTENSION_SELECTOR"
#endif
#endif /* (SSH_INTERCEPTOR_NUM_EXTENSION_SELECTORS > 0) */
#endif /* SSH_LINUX_FWMARK_EXTENSION_SELECTOR */


/****************************** Internal defines *****************************/

#define SSH_LINUX_INTERCEPTOR_NR_CPUS NR_CPUS

/* Flags for ssh_engine_start */
#define SSH_LINUX_ENGINE_FLAGS 0

#define SSH_LINUX_INTERCEPTOR_MODULE_DESCRIPTION "VPNClient"

/********************** Kernel version specific wrapper macros ***************/

#ifdef LINUX_HAS_DEV_GET_FLAGS
#define SSH_LINUX_DEV_GET_FLAGS(dev) dev_get_flags(dev)
#else /* LINUX_HAS_DEV_GET_FLAGS */
#define SSH_LINUX_DEV_GET_FLAGS(dev) ((dev)->flags)
#endif /* LINUX_HAS_DEV_GET_FLAGS */

#ifdef LINUX_NF_HOOK_SKB_IS_POINTER
typedef struct sk_buff SshHookSkb;
#define SSH_HOOK_SKB_PTR(_skb) _skb
#else  /* LINUX_NF_HOOK_SKB_IS_POINTER */
typedef struct sk_buff *SshHookSkb;
#define SSH_HOOK_SKB_PTR(_skb) *_skb
#endif /* LINUX_NF_HOOK_SKB_IS_POINTER */

#ifdef LINUX_HAS_SKB_MARK
#define SSH_SKB_MARK(__skb) ((__skb)->mark)
#else /* LINUX_HAS_SKB_MARK */
#define SSH_SKB_MARK(__skb) ((__skb)->nfmark)
#endif /* LINUX_HAS_SKB_MARK */

#ifdef LINUX_HAS_DST_MTU
#define SSH_DST_MTU(__dst) (dst_mtu((__dst)))
#else /* LINUX_HAS_DST_MTU */
#define SSH_DST_MTU(__dst) (dst_pmtu((__dst)))
#endif /* LINUX_HAS_DST_MTU */

/* Before 2.6.22 kernels, the net devices were accessed
   using directly global variables.
   2.6.22 -> 2.6.23 introduced new functions accessing
   the net device list.
   2.6.24 -> these new functions started taking new
   arguments. */
#ifndef LINUX_HAS_NETDEVICE_ACCESSORS
/* For 2.4.x -> 2.6.21 kernels */
#define SSH_FIRST_NET_DEVICE()     dev_base
#define SSH_NEXT_NET_DEVICE(_dev)  _dev->next

#else /* LINUX_HAS_NETDEVICE_ACCESSORS */
#ifndef LINUX_NET_DEVICE_HAS_ARGUMENT
/* For 2.6.22 -> 2.6.23 kernels */
#define SSH_FIRST_NET_DEVICE()     first_net_device()
#define SSH_NEXT_NET_DEVICE(_dev)  next_net_device(_dev)

#else /* LINUX_NET_DEVICE_HAS_ARGUMENT */
/* For 2.6.24 -> kernels */
#define SSH_FIRST_NET_DEVICE()     first_net_device(&init_net)
#define SSH_NEXT_NET_DEVICE(_dev)  next_net_device(_dev)

#endif /* LINUX_NET_DEVICE_HAS_ARGUMENT */
#endif /* LINUX_HAS_NETDEVICE_ACCESSORS */

/* This HAVE_NET_DEVICE_OPS was removed in 3.1.x */
#ifdef LINUX_HAS_NET_DEVICE_OPS
#ifndef HAVE_NET_DEVICE_OPS
#define HAVE_NET_DEVICE_OPS 1
#endif /* HAVE_NET_DEVICE_OPS */
#endif /* LINUX_HAS_NET_DEVICE_OPS */

#ifdef LINUX_NET_DEVICE_HAS_ARGUMENT
#define SSH_DEV_GET_BY_INDEX(_i) dev_get_by_index(&init_net, (_i))
#else /* LINUX_NET_DEVICE_HAS_ARGUMENT */
#define SSH_DEV_GET_BY_INDEX(_i) dev_get_by_index((_i))
#endif /* LINUX_NET_DEVICE_HAS_ARGUMENT */

#ifdef LINUX_HAS_SKB_DATA_ACCESSORS
/* On new kernel versions the skb->end, skb->tail, skb->network_header,
   skb->mac_header, and skb->transport_header are either pointers to
   skb->data (on 32bit platforms) or offsets from skb->data
   (on 64bit platforms). */

#define SSH_SKB_GET_END(__skb) (skb_end_pointer((__skb)))

#define SSH_SKB_GET_TAIL(__skb) (skb_tail_pointer((__skb)))
#define SSH_SKB_SET_TAIL(__skb, __ptr) \
   (skb_set_tail_pointer((__skb), (__ptr) - (__skb)->data))
#define SSH_SKB_RESET_TAIL(__skb) (skb_reset_tail_pointer((__skb)))

#define SSH_SKB_GET_NETHDR(__skb) (skb_network_header((__skb)))
#define SSH_SKB_SET_NETHDR(__skb, __ptr) \
   (skb_set_network_header((__skb), (__ptr) - (__skb)->data))
#define SSH_SKB_RESET_NETHDR(__skb) (skb_reset_network_header((__skb)))

#define SSH_SKB_GET_MACHDR(__skb) (skb_mac_header((__skb)))
#define SSH_SKB_SET_MACHDR(__skb, __ptr) \
   (skb_set_mac_header((__skb), (__ptr) - (__skb)->data))
#define SSH_SKB_RESET_MACHDR(__skb) (skb_reset_mac_header((__skb)))

#define SSH_SKB_GET_TRHDR(__skb) (skb_transport_header((__skb)))
#define SSH_SKB_SET_TRHDR(__skb, __ptr) \
   (skb_set_transport_header((__skb), (__ptr) - (__skb)->data))
#define SSH_SKB_RESET_TRHDR(__skb) (skb_reset_transport_header((__skb)))

#else  /* LINUX_HAS_SKB_DATA_ACCESSORS */

#define SSH_SKB_GET_END(__skb) ((__skb)->end)

#define SSH_SKB_GET_TAIL(__skb) ((__skb)->tail)
#define SSH_SKB_SET_TAIL(__skb, __ptr) ((__skb)->tail = (__ptr))
#define SSH_SKB_RESET_TAIL(__skb) ((__skb)->tail = NULL)

#define SSH_SKB_GET_NETHDR(__skb) ((__skb)->nh.raw)
#define SSH_SKB_SET_NETHDR(__skb, __ptr) ((__skb)->nh.raw = (__ptr))
#define SSH_SKB_RESET_NETHDR(__skb) ((__skb)->nh.raw = NULL)

#define SSH_SKB_GET_MACHDR(__skb) ((__skb)->mac.raw)
#define SSH_SKB_SET_MACHDR(__skb, __ptr) ((__skb)->mac.raw = (__ptr))
#define SSH_SKB_RESET_MACHDR(__skb) ((__skb)->mac.raw = NULL)

#define SSH_SKB_GET_TRHDR(__skb) ((__skb)->h.raw)
#define SSH_SKB_SET_TRHDR(__skb, __ptr) ((__skb)->h.raw = (__ptr))
#define SSH_SKB_RESET_TRHDR(__skb) ((__skb)->h.raw = NULL)

#endif /* LINUX_HAS_SKB_DATA_ACCESSORS */

#ifdef LINUX_HAS_SKB_CSUM_OFFSET
/* On linux-2.6.20 and later skb->csum is split into
   a union of csum and csum_offset. */
#define SSH_SKB_CSUM_OFFSET(__skb) ((__skb)->csum_offset)
#define SSH_SKB_CSUM(__skb) ((__skb)->csum)
#else /* LINUX_HAS_SKB_CSUM_OFFSET */
#define SSH_SKB_CSUM_OFFSET(__skb) ((__skb)->csum)
#define SSH_SKB_CSUM(__skb) ((__skb)->csum)
#endif /* LINUX_HAS_SKB_CSUM_OFFSET */

#ifdef LINUX_NF_INET_HOOKNUMS

#define SSH_NF_IP_PRE_ROUTING NF_INET_PRE_ROUTING
#define SSH_NF_IP_LOCAL_IN NF_INET_LOCAL_IN
#define SSH_NF_IP_FORWARD NF_INET_FORWARD
#define SSH_NF_IP_LOCAL_OUT NF_INET_LOCAL_OUT
#define SSH_NF_IP_POST_ROUTING NF_INET_POST_ROUTING
#define SSH_NF_IP_PRI_FIRST INT_MIN

#define SSH_NF_IP6_PRE_ROUTING NF_INET_PRE_ROUTING
#define SSH_NF_IP6_LOCAL_IN NF_INET_LOCAL_IN
#define SSH_NF_IP6_FORWARD NF_INET_FORWARD
#define SSH_NF_IP6_LOCAL_OUT NF_INET_LOCAL_OUT
#define SSH_NF_IP6_POST_ROUTING NF_INET_POST_ROUTING
#define SSH_NF_IP6_PRI_FIRST INT_MIN

#else /* LINUX_UNIFIED_NETFILTER_IP_HOOKNUMS */

#define SSH_NF_IP_PRE_ROUTING NF_IP_PRE_ROUTING
#define SSH_NF_IP_LOCAL_IN NF_IP_LOCAL_IN
#define SSH_NF_IP_FORWARD NF_IP_FORWARD
#define SSH_NF_IP_LOCAL_OUT NF_IP_LOCAL_OUT
#define SSH_NF_IP_POST_ROUTING NF_IP_POST_ROUTING
#define SSH_NF_IP_PRI_FIRST NF_IP_PRI_FIRST

#define SSH_NF_IP6_PRE_ROUTING NF_IP6_PRE_ROUTING
#define SSH_NF_IP6_LOCAL_IN NF_IP6_LOCAL_IN
#define SSH_NF_IP6_FORWARD NF_IP6_FORWARD
#define SSH_NF_IP6_LOCAL_OUT NF_IP6_LOCAL_OUT
#define SSH_NF_IP6_POST_ROUTING NF_IP6_POST_ROUTING
#define SSH_NF_IP6_PRI_FIRST NF_IP6_PRI_FIRST

#endif /* LINUX_NF_INET_HOOKNUMS */

#ifdef LINUX_HAS_NFPROTO_ARP
#define SSH_NFPROTO_ARP NFPROTO_ARP
#else /* LINUX_HAS_NFPROTO_ARP */
#define SSH_NFPROTO_ARP NF_ARP
#endif /* LINUX_HAS_NFPROTO_ARP */

/*
  Since 2.6.31 there is now skb->dst pointer and
  functions skb_dst() and skb_dst_set() should be used.

  The code is modified to use the functions. For older
  version corresponding macros are defined.
 */
#ifdef LINUX_HAS_SKB_DST_FUNCTIONS
#define SSH_SKB_DST(__skb) skb_dst((__skb))
#define SSH_SKB_DST_SET(__skb, __dst) skb_dst_set((__skb), (__dst))
#else /* LINUX_HAS_SKB_DST_FUNCTIONS */
#define SSH_SKB_DST(__skb) ((__skb)->dst)
#define SSH_SKB_DST_SET(__skb, __dst) ((void)((__skb)->dst = (__dst)))
#endif /* LINUX_HAS_SKB_DST_FUNCTIONS */

#ifdef IP6CB
#define SSH_LINUX_IP6CB(skbp) IP6CB(skbp)
#else /* IP6CB */
#define SSH_LINUX_IP6CB(skbp) ((struct inet6_skb_parm *) ((skbp)->cb))
#endif /* IP6CB */

/* Stating from linux 2.6.35 the IPv6 address list needs to be iterated
   using the list_for_each_* macros. */
#ifdef LINUX_RT_DST_IS_NOT_IN_UNION
#define SSH_RT_DST(_rt) ((_rt)->dst)
#else /* LINUX_RT_DST_IS_NOT_IN_UNION */
#define SSH_RT_DST(_rt) ((_rt)->u.dst)
#endif /* LINUX_RT_DST_IS_NOT_IN_UNION */

/* Starting from linux 2.6.35 the IPv6 address list needs to be iterated
   using the list_for_each_* macros. */
#ifdef LINUX_HAS_INET6_IFADDR_LIST_HEAD
#define SSH_INET6_IFADDR_LIST_FOR_EACH(item, next, list)	        \
  list_for_each_entry_safe((item), (next), &(list), if_list)
#else /* LINUX_HAS_INET6_IFADDR_LIST_HEAD */
#define SSH_INET6_IFADDR_LIST_FOR_EACH(item, next, list)		\
  for ((item) = (list), (next) = NULL;					\
       (item) != NULL;							\
       (item) = (item)->if_next)
#endif /* LINUX_HAS_INET6_IFADDR_LIST_HEAD */

#if defined(LINUX_DST_ALLOC_HAS_MANY_ARGS)
#define SSH_DST_ALLOC(_dst) dst_alloc((_dst)->ops, NULL, 0, 0, 0)
#elif defined(LINUX_DST_ALLOC_HAS_REFCOUNT)
#define SSH_DST_ALLOC(_dst) dst_alloc((_dst)->ops, 0)
#else /* defined(LINUX_DST_ALLOC_HAS_REFCOUNT) */
#define SSH_DST_ALLOC(_dst) dst_alloc((_dst)->ops)
#endif /* defined(LINUX_DST_ALLOC_HAS_REFCOUNT) */

#if defined(LINUX_SSH_RTABLE_FIRST_ELEMENT_NEEDED)
#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,38)
#define SSH_RTABLE_FIRST_MEMBER(_rt) ((_rt)->fl)
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)
#define SSH_RTABLE_FIRST_MEMBER(_rt) ((_rt)->rt_key_dst)
#endif /* LINUX_VERSION_CODE */
#endif /* defined(LINUX_SSH_RTABLE_FIRST_ELEMENT_NEEDED) */

/* Linux 2.6.39 removed fl4_dst and fl6_dst defines. We like to use
   those so redefinig those for our purposes. */
#ifdef LINUX_FLOWI_NO_FL4_ACCESSORS
#define fl4_dst u.ip4.daddr
#define fl4_src	u.ip4.saddr
#define fl4_tos	u.ip4.__fl_common.flowic_tos
#define oif u.ip4.__fl_common.flowic_oif
#define proto u.ip4.__fl_common.flowic_proto
#define fl4_scope u.ip4.__fl_common.flowic_scope
#endif /* LINUX_FLOWI_NO_FL4_ACCESSORS */

#ifdef LINUX_FLOWI_NO_FL6_ACCESSORS
#define fl6_dst u.ip6.daddr
#define fl6_src	u.ip6.saddr
#endif /* LINUX_FLOWI_NO_FL6_ACCESSORS */

/****************************** Statistics helper macros *********************/

#ifdef DEBUG_LIGHT

#define SSH_LINUX_STATISTICS(interceptor, block)         \
do                                                       \
  {                                                      \
    if ((interceptor))                                   \
      {                                                  \
        spin_lock_bh(&(interceptor)->statistics_lock);   \
        block;                                           \
        spin_unlock_bh(&(interceptor)->statistics_lock); \
      }                                                  \
  }                                                      \
while (0)

#else /* DEBUG_LIGHT */

#define SSH_LINUX_STATISTICS(interceptor, block)

#endif /* DEBUG_LIGHT */

/****************************** Interface handling ***************************/

/* Sanity check that the interface index 'ifnum' fits into
   the SshInterceptorIfnum data type. 'ifnum' may be equal to
   SSH_INTERCEPTOR_INVALID_IFNUM. */
#define SSH_LINUX_ASSERT_IFNUM(ifnum) \
SSH_ASSERT(((SshUInt32) (ifnum)) < ((SshUInt32) SSH_INTERCEPTOR_MAX_IFNUM) \
|| ((SshUInt32) (ifnum)) == ((SshUInt32) SSH_INTERCEPTOR_INVALID_IFNUM))

/* Sanity check that the interface index 'ifnum' is a valid
   SshInterceptorIfnum. */
#define SSH_LINUX_ASSERT_VALID_IFNUM(ifnum) \
SSH_ASSERT(((SshUInt32) (ifnum)) < ((SshUInt32) SSH_INTERCEPTOR_MAX_IFNUM) \
&& ((SshUInt32) (ifnum)) != ((SshUInt32) SSH_INTERCEPTOR_INVALID_IFNUM))

/* Interface structure for caching "ifindex->dev" mapping. */
typedef struct SshInterceptorInternalInterfaceRec
*SshInterceptorInternalInterface;

struct SshInterceptorInternalInterfaceRec
{
  /* Next entry in the hashtable chain */
  SshInterceptorInternalInterface next;
  /* Interface index */
  SshUInt32 ifindex;
  /* Linux net_device structure */
  struct net_device *dev;

  /* This field is used to mark existing interfaces,
     and to remove disappeared interfaces from the hashtable. */
  SshUInt8 generation;

  /* Pointer to private data. This is currently used only by Octeon. */
  void *context;
};

/* Number of hashtable slots in the interface hashtable. */
#define SSH_LINUX_IFACE_HASH_SIZE 256

/* Maximum number of entries in the interface hashtable.
   Currently equal to maximum interface number. */
#define SSH_LINUX_IFACE_TABLE_SIZE SSH_INTERCEPTOR_MAX_IFNUM

/****************************** Proc entries *********************************/

#define SSH_PROC_ROOT "vpnclient"
#define SSH_PROC_ENGINE "engine"
#define SSH_PROC_VERSION "version"

/* Ipm receive buffer size. This must be big enough to fit a maximum sized
   IP packet + internal packet data + ipm message header. There is only
   one receive buffer. */
#define SSH_LINUX_IPM_RECV_BUFFER_SIZE 66000

/* Ipm channel message structure. These structures are used for queueing
   messages from kernel to userspace. The maximum number of allocated messages
   is limited by SSH_LINUX_MAX_IPM_MESSAGES (in linux_params.h). */
typedef struct SshInterceptorIpmMsgRec
SshInterceptorIpmMsgStruct, *SshInterceptorIpmMsg;

struct SshInterceptorIpmMsgRec
{
  /* Send queue is doubly linked, freelist uses only `next'. */
  SshInterceptorIpmMsg next;
  SshInterceptorIpmMsg prev;

  SshUInt8 reliable : 1; /* message is reliable. */
  SshUInt8 emergency_mallocated : 1; /* message is allocated from heap */

  /* Offset for partially sent message */
  size_t offset;

  /* Message length and data. */
  size_t len;
  unsigned char *buf;
};

/* Ipm structure */
typedef struct SshInterceptorIpmRec
{
  /* RW lock for protecting the send message queue and the message freelist. */
  rwlock_t lock;

  /* Is ipm channel open */
  atomic_t open;

  /* Message freelist and number of allocated messages. */
  SshInterceptorIpmMsg msg_freelist;
  SshUInt32 msg_allocated;

  /* Output message queue */
  SshInterceptorIpmMsg send_queue;
  SshInterceptorIpmMsg send_queue_tail;

  /* Number of unreliable messages in the sed queue. */
  SshUInt32 send_queue_num_unreliable;

} SshInterceptorIpmStruct, *SshInterceptorIpm;


/* Structure for ipm channel /proc entry. */
typedef struct SshInterceptorIpmProcEntryRec
{
  /* /proc filesystem inode */
  struct proc_dir_entry *entry;

  /* RW lock for protecting the proc entry */
  rwlock_t lock;

  /* Is an userspace application using this entry */
  Boolean open;

  /* Is another read ongoing? When this is TRUE
     then `send_msg' is owned by the reader. */
  Boolean read_active;

  /* Is another write ongoing? When this is TRUE
     then `recv_buf' is owned by the writer. */
  Boolean write_active;

  /* Wait queue for blocking mode reads and writes. */
  wait_queue_head_t wait_queue;

  /* Output message under processing. */
  SshInterceptorIpmMsg send_msg;

  /* Input message length */
  size_t recv_len;

  /* Input message buffer */
  size_t recv_buf_size;
  unsigned char *recv_buf;

} SshInterceptorIpmProcEntryStruct, *SshInterceptorIpmProcEntry;


/* Structure for other /proc entries. */
typedef struct SshInterceptorProcEntryRec
{
  /* /proc filesystem entry */
  struct proc_dir_entry *entry;

  /* RW lock for protecting the proc entry */
  rwlock_t lock;

  /* Is an userspace application using this entry */
  Boolean open;

  /* Is another read or write ongoing? When this is TRUE
     then `buf' is owned by the reader/writer. */
  Boolean active;

  /* Number of bytes returned to the userpace application */
  size_t buf_len;

  /* Preallocated buffer for read and write operations. */
  char buf[1024];

} SshInterceptorProcEntryStruct, *SshInterceptorProcEntry;

/****************************** Dst entry cache ******************************/
#define SSH_DST_ENTRY_TBL_SIZE 128
typedef struct SshDstEntryRec
{
  struct dst_entry *dst_entry;

  unsigned long allocation_time;
  SshUInt32 dst_entry_id;

  struct SshDstEntryRec *next;
} *SshDstEntry, SshDstEntryStruct;


/****************************** Interceptor Object ***************************/

struct SshInterceptorRec
{
  /* Function pointers to netfilter infrastructure */
  struct
  {
    int (*ip_rcv_finish) (struct sk_buff *);
    int (*ip_finish_output) (struct sk_buff *);
#ifdef SSH_LINUX_INTERCEPTOR_IPV6
    int (*ip6_rcv_finish) (struct sk_buff *);
    int (*ip6_output_finish) (struct sk_buff *);
#endif /* SSH_LINUX_INTERCEPTOR_IPV6 */
#ifndef SSH_IPSEC_IP_ONLY_INTERCEPTOR
    int (*arp_process) (struct sk_buff *);
#endif /* !SSH_IPSEC_IP_ONLY_INTERCEPTOR */
  } linux_fn;

  SshVirtualAdapter virtual_adapters[SSH_LINUX_MAX_VIRTUAL_ADAPTERS];

  Boolean hooks_installed;

  /* Interface information used in ssh_interceptor_send()
     (and elsewhere obviously, but the aforementioned
     is the reason it is here). 'if_hash', 'if_table_size',
     and 'if_generation' are protected by 'if_table_lock' rwlock. */
  SshInterceptorInternalInterface if_hash[SSH_LINUX_IFACE_HASH_SIZE];

  SshInterceptorInternalInterface if_table;
  SshUInt32 if_table_size;
  SshUInt8 if_generation;

  /* Protected by interceptor_lock */
  int num_interface_callbacks;

  /* Notifiers, notifies when interfaces change. */
  Boolean iface_notifiers_installed;

  struct notifier_block notifier_netdev;
  struct notifier_block notifier_inetaddr;
#ifdef SSH_LINUX_INTERCEPTOR_IPV6
  struct notifier_block notifier_inet6addr;
#endif /* SSH_LINUX_INTERCEPTOR_IPV6 */

  /* Reader Writer lock for interface table manipulation */
  rwlock_t if_table_lock;

  /* Registered callbacks */

  /* Interface callback. Protected by 'interceptor_lock' */
  SshInterceptorInterfacesCB interfaces_callback;

  /* Unused and unprotected */
  SshInterceptorRouteChangeCB route_callback;

  /* Callback for packet. Protected by rcu. */
  SshInterceptorPacketCB packet_callback;

  /* Context for interface, route and packet callbacks. */
  void *callback_context;

  /* Engine context */
  SshEngine engine;
  Boolean engine_open;

  /* Intercept packets. */
  Boolean enable_interception;

  /* Name of related engine-instance */
  char *name;

  /* Ipm channel */
  SshInterceptorIpmStruct ipm;

  /* /proc filesystem entries */
  struct proc_dir_entry *proc_dir;

  SshInterceptorIpmProcEntryStruct ipm_proc_entry;

  SshInterceptorProcEntryStruct version_proc_entry;

  /* Main mutex for interceptor use */
  SshKernelMutex interceptor_lock;

  /* Mutex for memory map manipulation */
  SshKernelMutex memory_lock;

  /* Mutex for packet handling */
  SshKernelMutex packet_lock;

  SshKernelMutex dst_entry_cache_lock;
  Boolean dst_entry_cache_timeout_registered;
  SshUInt32 dst_entry_id;
  struct timer_list dst_cache_timer;
  SshUInt32 dst_entry_cached_items;
  SshDstEntry dst_entry_table[SSH_DST_ENTRY_TBL_SIZE];

#ifdef DEBUG_LIGHT
  /* Statistics spin lock */
  spinlock_t statistics_lock;

  struct {
    /* Statistics */
    SshUInt64 num_packets_out;
    SshUInt64 num_packets_in;
    SshUInt64 num_bytes_out;
    SshUInt64 num_bytes_in;
    SshUInt64 num_passthrough;
    SshUInt64 num_fastpath_packets_in;
    SshUInt64 num_fastpath_packets_out;
    SshUInt64 num_fastpath_bytes_in;
    SshUInt64 num_fastpath_bytes_out;
    SshUInt64 num_errors;
    SshUInt64 num_packets_sent;
    SshUInt64 num_bytes_sent;
    SshUInt64 allocated_memory;
    SshUInt64 allocated_memory_max;
    SshUInt64 num_allocations;
    SshUInt64 num_allocations_large;
    SshUInt64 num_allocations_total;
    SshUInt64 num_allocated_packets;
    SshUInt64 num_allocated_packets_total;
    SshUInt64 num_copied_packets;
    SshUInt64 num_failed_allocs;
    SshUInt64 num_light_locks;
    SshUInt64 num_light_interceptor_locks;
    SshUInt64 num_heavy_locks;
    SshUInt64 ipm_send_queue_len;
    SshUInt64 ipm_send_queue_bytes;
  } stats;
#endif /* DEBUG_LIGHT */
};

typedef struct SshInterceptorRec SshInterceptorStruct;

/****************************** Function prototypes **************************/

/* Call packet_callback */
#define SSH_LINUX_INTERCEPTOR_PACKET_CALLBACK(interceptor, pkt)		    \
  do {									    \
    rcu_read_lock();							    \
    (interceptor)->packet_callback((pkt), (interceptor)->callback_context); \
    rcu_read_unlock();							    \
  } while (0)

/* Proc entries */
Boolean ssh_interceptor_proc_init(SshInterceptor interceptor);
void ssh_interceptor_proc_uninit(SshInterceptor interceptor);

/* Ipm channel */

/* init / uninit */
Boolean ssh_interceptor_ipm_init(SshInterceptor interceptor);
void ssh_interceptor_ipm_uninit(SshInterceptor interceptor);

/* open / close. These functions handle ipm message queue flushing. */
void interceptor_ipm_open(SshInterceptor interceptor);
void interceptor_ipm_close(SshInterceptor interceptor);

/* open / close notifiers. These functions notify engine. */
void ssh_interceptor_notify_ipm_open(SshInterceptor interceptor);
void ssh_interceptor_notify_ipm_close(SshInterceptor interceptor);

Boolean ssh_interceptor_send_to_ipm(unsigned char *data, size_t len,
				    Boolean reliable, void *machine_context);
ssize_t ssh_interceptor_receive_from_ipm(unsigned char *data, size_t len);

void interceptor_ipm_message_free(SshInterceptor interceptor,
                                  SshInterceptorIpmMsg msg);

/* Packet access and manipulation. */

/* Header-only allocation.
   This function will assert that the interface numbers will
   fit into the data type SshInterceptorIfnum. */
SshInterceptorInternalPacket
ssh_interceptor_packet_alloc_header(SshInterceptor interceptor,
                                    SshUInt32 flags,
                                    SshInterceptorProtocol protocol,
                                    SshUInt32 ifnum_in,
                                    SshUInt32 ifnum_out,
                                    struct sk_buff *skb,
                                    Boolean force_copy_skbuff,
                                    Boolean free_original_on_copy,
				    Boolean packet_from_system);



/* Allocates new packet skb with copied data from original
   + the extra free space reserved for extensions. */
struct sk_buff *
ssh_interceptor_packet_skb_dup(SshInterceptor interceptor,
			       struct sk_buff *skb,
                               size_t addbytes_active_ofs,
                               size_t addbytes_active);

/* Align the interceptor packet at the data offset 'offset' to a word
   boundary. On failure, 'pp' is freed and returns FALSE. */
Boolean
ssh_interceptor_packet_align(SshInterceptorPacket packet, size_t offset);

/* Verify that `skbp' has enough headroom to be sent out through `skbp->dev'.
   On failure this frees `skbp' and returns NULL. */
struct sk_buff *
ssh_interceptor_packet_verify_headroom(struct sk_buff *skbp,
				       size_t media_header_len);

void
ssh_interceptor_packet_return_dst_entry(SshInterceptor interceptor,
					SshUInt32 dst_entry_id,
					SshInterceptorPacket pp,
					Boolean remove_only);
SshUInt32
ssh_interceptor_packet_cache_dst_entry(SshInterceptor interceptor,
				       SshInterceptorPacket pp);

Boolean
ssh_interceptor_dst_entry_cache_init(SshInterceptor interceptor);

void
ssh_interceptor_dst_entry_cache_flush(SshInterceptor interceptor);

void
ssh_interceptor_dst_entry_cache_uninit(SshInterceptor interceptor);

/* Packet freelist init / uninit. */
Boolean ssh_interceptor_packet_freelist_init(SshInterceptor interceptor);
void ssh_interceptor_packet_freelist_uninit(SshInterceptor interceptor);

int ssh_linux_module_inc_use_count(void);
void ssh_linux_module_dec_use_count(void);


Boolean ssh_interceptor_ip_glue_init(SshInterceptor interceptor);
Boolean ssh_interceptor_ip_glue_uninit(SshInterceptor interceptor);

int ssh_interceptor_hook_magic_init(void);

struct net_device *
ssh_interceptor_ifnum_to_netdev(SshInterceptor interceptor, SshUInt32 ifnum);
struct net_device *
ssh_interceptor_ifnum_to_netdev_ctx(SshInterceptor interceptor,
				    SshUInt32 ifnum, void **context_return);
void ssh_interceptor_release_netdev(struct net_device *dev);
void ssh_interceptor_receive_ifaces(SshInterceptor interceptor);
void ssh_interceptor_clear_ifaces(SshInterceptor interceptor);
Boolean ssh_interceptor_iface_init(SshInterceptor interceptor);
void ssh_interceptor_iface_uninit(SshInterceptor interceptor);

/* skb rerouting */
Boolean ssh_interceptor_reroute_skb_ipv4(SshInterceptor interceptor,
					 struct sk_buff *skb,
					 SshUInt16 route_selector,
					 SshUInt32 ifnum_in);
#ifdef SSH_LINUX_INTERCEPTOR_IPV6
Boolean ssh_interceptor_reroute_skb_ipv6(SshInterceptor interceptor,
					 struct sk_buff *skb,
					 SshUInt16 route_selector,
					 SshUInt32 ifnum_in);
#endif /* SSH_LINUX_INTERCEPTOR_IPV6 */

#endif /* LINUX_INTERNAL_H */