1 | /* |
---|
2 | * Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com> |
---|
3 | * |
---|
4 | * This program is free software; you can redistribute it and/or modify |
---|
5 | * it under the terms of the GNU General Public License as published by the |
---|
6 | * Free Software Foundation; either version 2 of the License, or (at your |
---|
7 | * option) any later version. |
---|
8 | * |
---|
9 | * This program is distributed in the hope that it will be useful, but |
---|
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
---|
11 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
---|
12 | * for more details. |
---|
13 | * |
---|
14 | * You should have received a copy of the GNU General Public License along |
---|
15 | * with this program; if not, write to the Free software Foundation, Inc., |
---|
16 | * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA |
---|
17 | * |
---|
18 | */ |
---|
19 | #ifdef __KERNEL__ |
---|
20 | |
---|
21 | #include <linux/config.h> |
---|
22 | #include <linux/module.h> |
---|
23 | #include <linux/init.h> |
---|
24 | #include <linux/skbuff.h> |
---|
25 | #include <linux/spinlock.h> |
---|
26 | |
---|
27 | #else |
---|
28 | |
---|
29 | #include "sys_kernel.h" |
---|
30 | #include "spinlock.h" |
---|
31 | #include "skbuff.h" |
---|
32 | |
---|
33 | #endif |
---|
34 | |
---|
35 | #include <tunnel.h> |
---|
36 | #include <vnet.h> |
---|
37 | #include <varp.h> |
---|
38 | #include "hash_table.h" |
---|
39 | |
---|
40 | #define MODULE_NAME "VNET" |
---|
41 | #define DEBUG 1 |
---|
42 | #undef DEBUG |
---|
43 | #include "debug.h" |
---|
44 | |
---|
45 | /** Table of tunnels, indexed by vnet and addr. */ |
---|
46 | HashTable *tunnel_table = NULL; |
---|
47 | rwlock_t tunnel_table_lock = RW_LOCK_UNLOCKED; |
---|
48 | |
---|
49 | #define tunnel_read_lock(flags) read_lock_irqsave(&tunnel_table_lock, (flags)) |
---|
50 | #define tunnel_read_unlock(flags) read_unlock_irqrestore(&tunnel_table_lock, (flags)) |
---|
51 | #define tunnel_write_lock(flags) write_lock_irqsave(&tunnel_table_lock, (flags)) |
---|
52 | #define tunnel_write_unlock(flags) write_unlock_irqrestore(&tunnel_table_lock, (flags)) |
---|
53 | |
---|
54 | void Tunnel_free(Tunnel *tunnel){ |
---|
55 | tunnel->type->close(tunnel); |
---|
56 | Tunnel_decref(tunnel->base); |
---|
57 | kfree(tunnel); |
---|
58 | } |
---|
59 | |
---|
60 | void Tunnel_print(Tunnel *tunnel){ |
---|
61 | if(tunnel){ |
---|
62 | iprintf("Tunnel<%p base=%p ref=%02d type=%s>\n", |
---|
63 | tunnel, |
---|
64 | tunnel->base, |
---|
65 | atomic_read(&tunnel->refcount), |
---|
66 | tunnel->type->name); |
---|
67 | if(tunnel->base){ |
---|
68 | Tunnel_print(tunnel->base); |
---|
69 | } |
---|
70 | } else { |
---|
71 | iprintf("Tunnel<%p base=%p ref=%02d type=%s>\n", |
---|
72 | NULL, NULL, 0, "ip"); |
---|
73 | } |
---|
74 | } |
---|
75 | |
---|
76 | int Tunnel_create(TunnelType *type, VnetId *vnet, VarpAddr *addr, |
---|
77 | Tunnel *base, Tunnel **val){ |
---|
78 | int err = 0; |
---|
79 | Tunnel *tunnel = NULL; |
---|
80 | if(!type || !type->open || !type->send || !type->close){ |
---|
81 | err = -EINVAL; |
---|
82 | goto exit; |
---|
83 | } |
---|
84 | tunnel = kmalloc(sizeof(Tunnel), GFP_ATOMIC); |
---|
85 | if(!tunnel){ |
---|
86 | err = -ENOMEM; |
---|
87 | goto exit; |
---|
88 | } |
---|
89 | atomic_set(&tunnel->refcount, 1); |
---|
90 | tunnel->key.vnet = *vnet; |
---|
91 | tunnel->key.addr = *addr; |
---|
92 | tunnel->type = type; |
---|
93 | tunnel->data = NULL; |
---|
94 | tunnel->send_stats = (TunnelStats){}; |
---|
95 | Tunnel_incref(base); |
---|
96 | tunnel->base = base; |
---|
97 | err = type->open(tunnel); |
---|
98 | exit: |
---|
99 | if(err && tunnel){ |
---|
100 | Tunnel_decref(tunnel); |
---|
101 | tunnel = NULL; |
---|
102 | } |
---|
103 | *val = tunnel; |
---|
104 | dprintf("< err=%d\n", err); |
---|
105 | return err; |
---|
106 | } |
---|
107 | |
---|
108 | void TunnelStats_update(TunnelStats *stats, int len, int err){ |
---|
109 | dprintf(">len=%d err=%d\n", len, err); |
---|
110 | if(err){ |
---|
111 | stats->dropped_bytes += len; |
---|
112 | stats->dropped_packets++; |
---|
113 | } else { |
---|
114 | stats->bytes += len; |
---|
115 | stats->packets++; |
---|
116 | } |
---|
117 | dprintf("<\n"); |
---|
118 | } |
---|
119 | |
---|
120 | static inline Hashcode tunnel_table_key_hash_fn(void *k){ |
---|
121 | return hash_hvoid(0, k, sizeof(TunnelKey)); |
---|
122 | } |
---|
123 | |
---|
124 | static int tunnel_table_key_equal_fn(void *k1, void *k2){ |
---|
125 | return memcmp(k1, k2, sizeof(TunnelKey)) == 0; |
---|
126 | } |
---|
127 | |
---|
128 | static void tunnel_table_entry_free_fn(HashTable *table, HTEntry *entry){ |
---|
129 | Tunnel *tunnel; |
---|
130 | if(!entry) return; |
---|
131 | tunnel = entry->value; |
---|
132 | Tunnel_decref(tunnel); |
---|
133 | HTEntry_free(entry); |
---|
134 | } |
---|
135 | |
---|
136 | int Tunnel_init(void){ |
---|
137 | int err = 0; |
---|
138 | dprintf(">\n"); |
---|
139 | tunnel_table = HashTable_new(0); |
---|
140 | if(!tunnel_table){ |
---|
141 | err = -ENOMEM; |
---|
142 | goto exit; |
---|
143 | } |
---|
144 | tunnel_table->entry_free_fn = tunnel_table_entry_free_fn; |
---|
145 | tunnel_table->key_size = sizeof(TunnelKey); |
---|
146 | tunnel_table->key_hash_fn = tunnel_table_key_hash_fn; |
---|
147 | tunnel_table->key_equal_fn = tunnel_table_key_equal_fn; |
---|
148 | exit: |
---|
149 | dprintf("< err=%d\n", err); |
---|
150 | return err; |
---|
151 | } |
---|
152 | |
---|
153 | /** Lookup tunnel state by vnet and destination. |
---|
154 | * The caller must drop the tunnel reference when done. |
---|
155 | * |
---|
156 | * @param vnet vnet |
---|
157 | * @param addr destination address |
---|
158 | * @return 0 on success |
---|
159 | */ |
---|
160 | int Tunnel_lookup(VnetId *vnet, VarpAddr *addr, Tunnel **tunnel){ |
---|
161 | unsigned long flags; |
---|
162 | TunnelKey key = { .vnet = *vnet, .addr = *addr }; |
---|
163 | dprintf(">\n"); |
---|
164 | tunnel_read_lock(flags); |
---|
165 | *tunnel = HashTable_get(tunnel_table, &key); |
---|
166 | tunnel_read_unlock(flags); |
---|
167 | Tunnel_incref(*tunnel); |
---|
168 | dprintf("< tunnel=%p\n", *tunnel); |
---|
169 | return (*tunnel ? 0 : -ENOENT); |
---|
170 | } |
---|
171 | |
---|
172 | /** Get a tunnel to a given vnet and destination, creating |
---|
173 | * a tunnel if necessary. |
---|
174 | * The caller must drop the tunnel reference when done. |
---|
175 | * |
---|
176 | * @param vnet vnet |
---|
177 | * @param addr destination address |
---|
178 | * @param ctor tunnel constructor |
---|
179 | * @parma ptunnel return parameter for the tunnel |
---|
180 | * @return 0 on success |
---|
181 | */ |
---|
182 | int Tunnel_open(VnetId *vnet, VarpAddr *addr, |
---|
183 | int (*ctor)(VnetId *vnet, VarpAddr *addr, Tunnel **ptunnel), |
---|
184 | Tunnel **ptunnel){ |
---|
185 | int err = 0; |
---|
186 | Tunnel *tunnel = NULL; |
---|
187 | unsigned long flags; |
---|
188 | TunnelKey key = { .vnet = *vnet, .addr = *addr }; |
---|
189 | |
---|
190 | tunnel_write_lock(flags); |
---|
191 | tunnel = HashTable_get(tunnel_table, &key); |
---|
192 | if(!tunnel){ |
---|
193 | err = ctor(vnet, addr, &tunnel); |
---|
194 | if(err) goto exit; |
---|
195 | if(!HashTable_add(tunnel_table, tunnel, tunnel)){ |
---|
196 | err = -ENOMEM; |
---|
197 | goto exit; |
---|
198 | } |
---|
199 | } |
---|
200 | exit: |
---|
201 | tunnel_write_unlock(flags); |
---|
202 | if(err){ |
---|
203 | Tunnel_decref(tunnel); |
---|
204 | *ptunnel = NULL; |
---|
205 | } else { |
---|
206 | Tunnel_incref(tunnel); |
---|
207 | *ptunnel = tunnel; |
---|
208 | } |
---|
209 | return err; |
---|
210 | } |
---|
211 | |
---|
212 | int Tunnel_add(Tunnel *tunnel){ |
---|
213 | int err = 0; |
---|
214 | unsigned long flags; |
---|
215 | dprintf(">\n"); |
---|
216 | tunnel_write_lock(flags); |
---|
217 | if(HashTable_add(tunnel_table, tunnel, tunnel)){ |
---|
218 | Tunnel_incref(tunnel); |
---|
219 | } else { |
---|
220 | err = -ENOMEM; |
---|
221 | } |
---|
222 | tunnel_write_unlock(flags); |
---|
223 | dprintf("< err=%d\n", err); |
---|
224 | return err; |
---|
225 | } |
---|
226 | |
---|
227 | int Tunnel_del(Tunnel *tunnel){ |
---|
228 | int err; |
---|
229 | unsigned long flags; |
---|
230 | tunnel_write_lock(flags); |
---|
231 | err = HashTable_remove(tunnel_table, tunnel); |
---|
232 | tunnel_write_unlock(flags); |
---|
233 | return err; |
---|
234 | } |
---|
235 | |
---|
236 | /** Do tunnel send processing on a packet. |
---|
237 | * |
---|
238 | * @param tunnel tunnel state |
---|
239 | * @param skb packet |
---|
240 | * @return 0 on success, error code otherwise |
---|
241 | */ |
---|
242 | int Tunnel_send(Tunnel *tunnel, struct sk_buff *skb){ |
---|
243 | int err = 0; |
---|
244 | dprintf("> tunnel=%p skb=%p\n", tunnel, skb); |
---|
245 | if(tunnel){ |
---|
246 | int len = skb->len; |
---|
247 | dprintf("> type=%s type->send...\n", tunnel->type->name); |
---|
248 | // Must not refer to skb after sending - might have been freed. |
---|
249 | err = tunnel->type->send(tunnel, skb); |
---|
250 | TunnelStats_update(&tunnel->send_stats, len, err); |
---|
251 | } else { |
---|
252 | err = skb_xmit(skb); |
---|
253 | } |
---|
254 | dprintf("< err=%d\n", err); |
---|
255 | return err; |
---|
256 | } |
---|
257 | |
---|
258 | int __init tunnel_module_init(void){ |
---|
259 | return Tunnel_init(); |
---|
260 | } |
---|
261 | |
---|
262 | void __exit tunnel_module_exit(void){ |
---|
263 | unsigned long flags; |
---|
264 | tunnel_write_lock(flags); |
---|
265 | if(tunnel_table){ |
---|
266 | HashTable_free(tunnel_table); |
---|
267 | tunnel_table = NULL; |
---|
268 | } |
---|
269 | tunnel_write_unlock(flags); |
---|
270 | } |
---|