blob: 54bdf0145fcde2063be9bc6388044d8430971c72 [file] [log] [blame]
Bart De Schuymer1abc55d2002-06-01 19:23:47 +00001/*
2 * communication.c, v2.0 April 2002
3 *
4 * Author: Bart De Schuymer
5 *
6 */
7
8// All the userspace/kernel communication is in this file.
9// The other code should not have to know anything about the way the
10// kernel likes the structure of the table data.
11// The other code works with linked lists, lots of linked lists.
12// So, the translation is done here.
13
14#include <getopt.h>
15#include <string.h>
16#include <errno.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <sys/socket.h>
20#include <linux/netfilter_bridge/ebtables.h>
21#include <linux/br_db.h> // the database
22#include <netinet/in.h> // IPPROTO_IP
23#include <asm/types.h>
24#include "include/ebtables_u.h"
25
26extern char* hooknames[NF_BR_NUMHOOKS];
27
28int sockfd = -1;
29
30void get_sockfd()
31{
32 if (sockfd == -1) {
33 sockfd = socket(AF_INET, SOCK_RAW, PF_INET);
34 if (sockfd < 0)
35 print_error("Problem getting a socket");
36 }
37}
38
39static struct ebt_replace * translate_user2kernel(struct ebt_u_replace *u_repl)
40{
41 struct ebt_replace *new;
42 struct ebt_u_entry *e;
43 struct ebt_u_match_list *m_l;
44 struct ebt_u_watcher_list *w_l;
45 char *p, *base;
46 int i, j;
47 unsigned int entries_size = 0;
48
49 new = (struct ebt_replace *)malloc(sizeof(struct ebt_replace));
50 if (!new)
51 print_memory();
52 new->valid_hooks = u_repl->valid_hooks;
53 memcpy(new->name, u_repl->name, sizeof(new->name));
54 new->nentries = u_repl->nentries;
55 new->num_counters = u_repl->num_counters;
56 new->counters = u_repl->counters;
57 memcpy(new->counter_entry, u_repl->counter_entry,
58 sizeof(new->counter_entry));
59 // determine size
60 for (i = 0; i < NF_BR_NUMHOOKS; i++) {
61 if (!(new->valid_hooks & (1 << i)))
62 continue;
63 entries_size += sizeof(struct ebt_entries);
64 j = 0;
65 e = u_repl->hook_entry[i]->entries;
66 while (e) {
67 j++;
68 entries_size += sizeof(struct ebt_entry);
69 m_l = e->m_list;
70 while (m_l) {
71 entries_size += m_l->m->match_size +
72 sizeof(struct ebt_entry_match);
73 m_l = m_l->next;
74 }
75 w_l = e->w_list;
76 while (w_l) {
77 entries_size += w_l->w->watcher_size +
78 sizeof(struct ebt_entry_watcher);
79 w_l = w_l->next;
80 }
81 entries_size += e->t->target_size +
82 sizeof(struct ebt_entry_target);
83 e = e->next;
84 }
85 // a little sanity check
86 if (j != u_repl->hook_entry[i]->nentries)
87 print_bug("Wrong nentries: %d != %d, hook = %s", j,
88 u_repl->hook_entry[i]->nentries, hooknames[i]);
89 }
90
91 new->entries_size = entries_size;
92 new->entries = (char *)malloc(entries_size);
93 if (!new->entries)
94 print_memory();
95
96 // put everything in one block
97 p = new->entries;
98 for (i = 0; i < NF_BR_NUMHOOKS; i++) {
99 struct ebt_entries *hlp;
100
101 if (!(new->valid_hooks & (1 << i)))
102 continue;
103 hlp = (struct ebt_entries *)p;
104 new->hook_entry[i] = hlp;
105 hlp->nentries = u_repl->hook_entry[i]->nentries;
106 hlp->policy = u_repl->hook_entry[i]->policy;
107 hlp->distinguisher = 0; // make the kernel see the light
108 p += sizeof(struct ebt_entries);
109 e = u_repl->hook_entry[i]->entries;
110 while (e) {
111 struct ebt_entry *tmp = (struct ebt_entry *)p;
112
113 tmp->bitmask = e->bitmask | EBT_ENTRY_OR_ENTRIES;
114 tmp->invflags = e->invflags;
115 tmp->ethproto = e->ethproto;
116 memcpy(tmp->in, e->in, sizeof(tmp->in));
117 memcpy(tmp->out, e->out, sizeof(tmp->out));
118 memcpy(tmp->logical_in, e->logical_in,
119 sizeof(tmp->logical_in));
120 memcpy(tmp->logical_out, e->logical_out,
121 sizeof(tmp->logical_out));
122 memcpy(tmp->sourcemac, e->sourcemac,
123 sizeof(tmp->sourcemac));
124 memcpy(tmp->sourcemsk, e->sourcemsk,
125 sizeof(tmp->sourcemsk));
126 memcpy(tmp->destmac, e->destmac, sizeof(tmp->destmac));
127 memcpy(tmp->destmsk, e->destmsk, sizeof(tmp->destmsk));
128
129 base = p;
130 p += sizeof(struct ebt_entry);
131 m_l = e->m_list;
132 while (m_l) {
133 memcpy(p, m_l->m, m_l->m->match_size +
134 sizeof(struct ebt_entry_match));
135 p += m_l->m->match_size +
136 sizeof(struct ebt_entry_match);
137 m_l = m_l->next;
138 }
139 tmp->watchers_offset = p - base;
140 w_l = e->w_list;
141 while (w_l) {
142 memcpy(p, w_l->w, w_l->w->watcher_size +
143 sizeof(struct ebt_entry_watcher));
144 p += w_l->w->watcher_size +
145 sizeof(struct ebt_entry_watcher);
146 w_l = w_l->next;
147 }
148 tmp->target_offset = p - base;
149 memcpy(p, e->t, e->t->target_size +
150 sizeof(struct ebt_entry_target));
151 p += e->t->target_size +
152 sizeof(struct ebt_entry_target);
153 tmp->next_offset = p - base;
154 e = e->next;
155 }
156 }
157
158 // sanity check
159 if (p - new->entries != new->entries_size)
160 print_bug("Entries_size bug");
161 return new;
162}
163
164void deliver_table(struct ebt_u_replace *u_repl)
165{
166 socklen_t optlen;
167 struct ebt_replace *repl;
168
169 // translate the struct ebt_u_replace to a struct ebt_replace
170 repl = translate_user2kernel(u_repl);
171 get_sockfd();
172 // give the data to the kernel
173 optlen = sizeof(struct ebt_replace) + repl->entries_size;
174 if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, repl, optlen))
Bart De Schuymer73564dc2002-06-05 18:13:51 +0000175 print_error("The kernel doesn't support a certain ebtables"
176 " extension, consider recompiling your kernel or insmod"
177 " the extension");
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000178}
179
180// gets executed after deliver_table
181void
182deliver_counters(struct ebt_u_replace *u_repl, unsigned short *counterchanges)
183{
184 unsigned short *point;
185 struct ebt_counter *old, *new, *newcounters;
186 socklen_t optlen;
187 struct ebt_replace repl;
188
189 if (u_repl->nentries == 0)
190 return;
191
192 newcounters = (struct ebt_counter *)
193 malloc(u_repl->nentries * sizeof(struct ebt_counter));
194 if (!newcounters)
195 print_memory();
196 memset(newcounters, 0, u_repl->nentries * sizeof(struct ebt_counter));
197 old = u_repl->counters;
198 new = newcounters;
199 point = counterchanges;
200 while (*point != CNT_END) {
201 if (*point == CNT_NORM) {
202 // 'normal' rule, meaning we didn't do anything to it
203 // So, we just copy
204 new->pcnt = old->pcnt;
205 // we've used an old counter
206 old++;
207 // we've set a new counter
208 new++;
209 } else
210 if (*point == CNT_DEL) {
211 // don't use this old counter
212 old++;
213 } else if (*point == CNT_ADD) {
214 // new counter, let it stay 0
215 new++;
216 } else {
217 // zero it
218 new->pcnt = 0;
219 old++;
220 new++;
221 }
222 point++;
223 }
224
225 free(u_repl->counters);
226 u_repl->counters = newcounters;
227 u_repl->num_counters = u_repl->nentries;
228 optlen = u_repl->nentries * sizeof(struct ebt_counter) +
229 sizeof(struct ebt_replace);
230 // now put the stuff in the kernel's struct ebt_replace
231 repl.counters = u_repl->counters;
232 repl.num_counters = u_repl->num_counters;
233 memcpy(repl.name, u_repl->name, sizeof(repl.name));
234
235 get_sockfd();
236 if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_COUNTERS, &repl, optlen))
237 print_bug("couldn't update kernel counters");
238}
239
240static int
241ebt_translate_match(struct ebt_entry_match *m, struct ebt_u_match_list ***l)
242{
243 struct ebt_u_match_list *new;
244
245 new = (struct ebt_u_match_list *)
246 malloc(sizeof(struct ebt_u_match_list));
247 if (!new)
248 print_memory();
249 new->m = (struct ebt_entry_match *)
250 malloc(m->match_size + sizeof(struct ebt_entry_match));
251 if (!new->m)
252 print_memory();
253 memcpy(new->m, m, m->match_size + sizeof(struct ebt_entry_match));
254 new->next = NULL;
255 **l = new;
256 *l = &new->next;
257 if (find_match(new->m->u.name) == NULL)
258 print_error("Kernel match %s unsupported by userspace tool",
259 new->m->u.name);
260 return 0;
261}
262
263static int
264ebt_translate_watcher(struct ebt_entry_watcher *w,
265 struct ebt_u_watcher_list ***l)
266{
267 struct ebt_u_watcher_list *new;
268
269 new = (struct ebt_u_watcher_list *)
270 malloc(sizeof(struct ebt_u_watcher_list));
271 if (!new)
272 print_memory();
273 new->w = (struct ebt_entry_watcher *)
274 malloc(w->watcher_size + sizeof(struct ebt_entry_watcher));
275 if (!new->w)
276 print_memory();
277 memcpy(new->w, w, w->watcher_size + sizeof(struct ebt_entry_watcher));
278 new->next = NULL;
279 **l = new;
280 *l = &new->next;
281 if (find_watcher(new->w->u.name) == NULL)
282 print_error("Kernel watcher %s unsupported by userspace tool",
283 new->w->u.name);
284 return 0;
285}
286
287static int
288ebt_translate_entry(struct ebt_entry *e, unsigned int *hook, int *n, int *cnt,
289 int *totalcnt, struct ebt_u_entry ***u_e, struct ebt_u_replace *u_repl,
290 unsigned int valid_hooks)
291{
292 // an entry
293 if (e->bitmask & EBT_ENTRY_OR_ENTRIES) {
294 struct ebt_u_entry *new;
295 struct ebt_u_match_list **m_l;
296 struct ebt_u_watcher_list **w_l;
297 struct ebt_entry_target *t;
298
299 new = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));
300 if (!new)
301 print_memory();
302 new->bitmask = e->bitmask;
303 // plain userspace code doesn't know about EBT_ENTRY_OR_ENTRIES
304 new->bitmask &= ~EBT_ENTRY_OR_ENTRIES;
305 new->invflags = e->invflags;
306 new->ethproto = e->ethproto;
307 memcpy(new->in, e->in, sizeof(new->in));
308 memcpy(new->out, e->out, sizeof(new->out));
309 memcpy(new->logical_in, e->logical_in,
310 sizeof(new->logical_in));
311 memcpy(new->logical_out, e->logical_out,
312 sizeof(new->logical_out));
313 memcpy(new->sourcemac, e->sourcemac, sizeof(new->sourcemac));
314 memcpy(new->sourcemsk, e->sourcemsk, sizeof(new->sourcemsk));
315 memcpy(new->destmac, e->destmac, sizeof(new->destmac));
316 memcpy(new->destmsk, e->destmsk, sizeof(new->destmsk));
317 new->m_list = NULL;
318 new->w_list = NULL;
319 new->next = NULL;
320 m_l = &new->m_list;
321 EBT_MATCH_ITERATE(e, ebt_translate_match, &m_l);
322 w_l = &new->w_list;
323 EBT_WATCHER_ITERATE(e, ebt_translate_watcher, &w_l);
324
325 t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
326 new->t = (struct ebt_entry_target *)
327 malloc(t->target_size + sizeof(struct ebt_entry_target));
328 if (!new->t)
329 print_memory();
330 if (find_target(t->u.name) == NULL)
331 print_error("Kernel target %s unsupported by "
332 "userspace tool", t->u.name);
333 memcpy(new->t, t, t->target_size +
334 sizeof(struct ebt_entry_target));
335
336 // I love pointers
337 **u_e = new;
338 *u_e = &new->next;
339 (*cnt)++;
340 (*totalcnt)++;
341 return 0;
342 } else { // a new chain
343 int i;
344 struct ebt_entries *entries = (struct ebt_entries *)e;
345 struct ebt_u_entries *new;
346
347 for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++)
348 if (valid_hooks & (1 << i))
349 break;
350 if (i >= NF_BR_NUMHOOKS)
351 print_bug("Not enough valid hooks");
352 *hook = i;
353 if (*n != *cnt)
354 print_bug("Nr of entries in the chain is wrong");
355 *n = entries->nentries;
356 *cnt = 0;
357 new = (struct ebt_u_entries *)
358 malloc(sizeof(struct ebt_u_entries));
359 if (!new)
360 print_memory();
361 new->nentries = entries->nentries;
362 new->policy = entries->policy;
363 new->entries = NULL;
364 u_repl->hook_entry[*hook] = new;
365 *u_e = &new->entries;
366 return 0;
367 }
368}
369
370// talk with kernel to receive the kernel's table
371void get_table(struct ebt_u_replace *u_repl)
372{
373 int i, j, k, hook;
374 socklen_t optlen;
375 struct ebt_replace repl;
376 struct ebt_u_entry **u_e;
377
378 get_sockfd();
379
380 optlen = sizeof(struct ebt_replace);
381 strcpy(repl.name, u_repl->name);
382 if (getsockopt(sockfd, IPPROTO_IP, EBT_SO_GET_INFO, &repl, &optlen))
Bart De Schuymer73564dc2002-06-05 18:13:51 +0000383 print_error("The %s table is not supported by the kernel,"
384 " consider recompiling your kernel or try insmod ebt_%s",
385 repl.name, repl.name);
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000386
387 if ( !(repl.entries = (char *) malloc(repl.entries_size)) )
388 print_memory();
389 if (repl.nentries) {
390 if (!(repl.counters = (struct ebt_counter *)
391 malloc(repl.nentries * sizeof(struct ebt_counter))) )
392 print_memory();
393 }
394 else
395 repl.counters = NULL;
396
397 // we want to receive the counters
398 repl.num_counters = repl.nentries;
399 optlen += repl.entries_size + repl.num_counters *
400 sizeof(struct ebt_counter);
401 if (getsockopt(sockfd, IPPROTO_IP, EBT_SO_GET_ENTRIES, &repl, &optlen))
402 print_bug("hmm, what is wrong??? bug#1");
403
404 // translate the struct ebt_replace to a struct ebt_u_replace
405 memcpy(u_repl->name, repl.name, sizeof(u_repl->name));
406 u_repl->valid_hooks = repl.valid_hooks;
407 u_repl->nentries = repl.nentries;
408 u_repl->num_counters = repl.num_counters;
409 u_repl->counters = repl.counters;
410 memcpy(u_repl->counter_entry, repl.counter_entry,
411 sizeof(repl.counter_entry));
412 hook = -1;
413 i = 0; // holds the expected nr. of entries for the chain
414 j = 0; // holds the up to now counted entries for the chain
415 k = 0; // holds the total nr. of entries,
416 // should equal u_repl->nentries afterwards
417 EBT_ENTRY_ITERATE(repl.entries, repl.entries_size, ebt_translate_entry,
418 &hook, &i, &j, &k, &u_e, u_repl, u_repl->valid_hooks);
419 if (k != u_repl->nentries)
420 print_bug("Wrong total nentries");
421}
422
423void get_dbinfo(struct brdb_dbinfo *nr)
424{
425 socklen_t optlen = sizeof(struct brdb_dbinfo);
426
427 get_sockfd();
428
429 if (getsockopt(sockfd, IPPROTO_IP, BRDB_SO_GET_DBINFO, nr, &optlen))
430 print_error("Sorry, br_db code probably not in kernel, "
431 "try insmod br_db");
432}
433
434void get_db(int len, struct brdb_dbentry *db)
435{
436 socklen_t optlen = len;
437
438 get_sockfd();
439
440 if ( getsockopt(sockfd, IPPROTO_IP, BRDB_SO_GET_DB, db, &optlen) ) {
441 print_bug("hmm, what is wrong??? bug#2");
442 }
443}
444
445void deliver_allowdb(__u16 *decision)
446{
447 socklen_t optlen = sizeof(__u16);
448
449 get_sockfd();
450
451 if (setsockopt(sockfd, IPPROTO_IP, BRDB_SO_SET_ALLOWDB,
452 decision, optlen))
453 print_error("Sorry, br_db code probably not in kernel, "
454 "try insmod br_db");
455}