blob: 02aff3f9e495faa5905793c3a844207cbac7f87c [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))
175 print_error("Couldn't update kernel chains, you probably need "
176 "to insmod an extension");
177}
178
179// gets executed after deliver_table
180void
181deliver_counters(struct ebt_u_replace *u_repl, unsigned short *counterchanges)
182{
183 unsigned short *point;
184 struct ebt_counter *old, *new, *newcounters;
185 socklen_t optlen;
186 struct ebt_replace repl;
187
188 if (u_repl->nentries == 0)
189 return;
190
191 newcounters = (struct ebt_counter *)
192 malloc(u_repl->nentries * sizeof(struct ebt_counter));
193 if (!newcounters)
194 print_memory();
195 memset(newcounters, 0, u_repl->nentries * sizeof(struct ebt_counter));
196 old = u_repl->counters;
197 new = newcounters;
198 point = counterchanges;
199 while (*point != CNT_END) {
200 if (*point == CNT_NORM) {
201 // 'normal' rule, meaning we didn't do anything to it
202 // So, we just copy
203 new->pcnt = old->pcnt;
204 // we've used an old counter
205 old++;
206 // we've set a new counter
207 new++;
208 } else
209 if (*point == CNT_DEL) {
210 // don't use this old counter
211 old++;
212 } else if (*point == CNT_ADD) {
213 // new counter, let it stay 0
214 new++;
215 } else {
216 // zero it
217 new->pcnt = 0;
218 old++;
219 new++;
220 }
221 point++;
222 }
223
224 free(u_repl->counters);
225 u_repl->counters = newcounters;
226 u_repl->num_counters = u_repl->nentries;
227 optlen = u_repl->nentries * sizeof(struct ebt_counter) +
228 sizeof(struct ebt_replace);
229 // now put the stuff in the kernel's struct ebt_replace
230 repl.counters = u_repl->counters;
231 repl.num_counters = u_repl->num_counters;
232 memcpy(repl.name, u_repl->name, sizeof(repl.name));
233
234 get_sockfd();
235 if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_COUNTERS, &repl, optlen))
236 print_bug("couldn't update kernel counters");
237}
238
239static int
240ebt_translate_match(struct ebt_entry_match *m, struct ebt_u_match_list ***l)
241{
242 struct ebt_u_match_list *new;
243
244 new = (struct ebt_u_match_list *)
245 malloc(sizeof(struct ebt_u_match_list));
246 if (!new)
247 print_memory();
248 new->m = (struct ebt_entry_match *)
249 malloc(m->match_size + sizeof(struct ebt_entry_match));
250 if (!new->m)
251 print_memory();
252 memcpy(new->m, m, m->match_size + sizeof(struct ebt_entry_match));
253 new->next = NULL;
254 **l = new;
255 *l = &new->next;
256 if (find_match(new->m->u.name) == NULL)
257 print_error("Kernel match %s unsupported by userspace tool",
258 new->m->u.name);
259 return 0;
260}
261
262static int
263ebt_translate_watcher(struct ebt_entry_watcher *w,
264 struct ebt_u_watcher_list ***l)
265{
266 struct ebt_u_watcher_list *new;
267
268 new = (struct ebt_u_watcher_list *)
269 malloc(sizeof(struct ebt_u_watcher_list));
270 if (!new)
271 print_memory();
272 new->w = (struct ebt_entry_watcher *)
273 malloc(w->watcher_size + sizeof(struct ebt_entry_watcher));
274 if (!new->w)
275 print_memory();
276 memcpy(new->w, w, w->watcher_size + sizeof(struct ebt_entry_watcher));
277 new->next = NULL;
278 **l = new;
279 *l = &new->next;
280 if (find_watcher(new->w->u.name) == NULL)
281 print_error("Kernel watcher %s unsupported by userspace tool",
282 new->w->u.name);
283 return 0;
284}
285
286static int
287ebt_translate_entry(struct ebt_entry *e, unsigned int *hook, int *n, int *cnt,
288 int *totalcnt, struct ebt_u_entry ***u_e, struct ebt_u_replace *u_repl,
289 unsigned int valid_hooks)
290{
291 // an entry
292 if (e->bitmask & EBT_ENTRY_OR_ENTRIES) {
293 struct ebt_u_entry *new;
294 struct ebt_u_match_list **m_l;
295 struct ebt_u_watcher_list **w_l;
296 struct ebt_entry_target *t;
297
298 new = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));
299 if (!new)
300 print_memory();
301 new->bitmask = e->bitmask;
302 // plain userspace code doesn't know about EBT_ENTRY_OR_ENTRIES
303 new->bitmask &= ~EBT_ENTRY_OR_ENTRIES;
304 new->invflags = e->invflags;
305 new->ethproto = e->ethproto;
306 memcpy(new->in, e->in, sizeof(new->in));
307 memcpy(new->out, e->out, sizeof(new->out));
308 memcpy(new->logical_in, e->logical_in,
309 sizeof(new->logical_in));
310 memcpy(new->logical_out, e->logical_out,
311 sizeof(new->logical_out));
312 memcpy(new->sourcemac, e->sourcemac, sizeof(new->sourcemac));
313 memcpy(new->sourcemsk, e->sourcemsk, sizeof(new->sourcemsk));
314 memcpy(new->destmac, e->destmac, sizeof(new->destmac));
315 memcpy(new->destmsk, e->destmsk, sizeof(new->destmsk));
316 new->m_list = NULL;
317 new->w_list = NULL;
318 new->next = NULL;
319 m_l = &new->m_list;
320 EBT_MATCH_ITERATE(e, ebt_translate_match, &m_l);
321 w_l = &new->w_list;
322 EBT_WATCHER_ITERATE(e, ebt_translate_watcher, &w_l);
323
324 t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
325 new->t = (struct ebt_entry_target *)
326 malloc(t->target_size + sizeof(struct ebt_entry_target));
327 if (!new->t)
328 print_memory();
329 if (find_target(t->u.name) == NULL)
330 print_error("Kernel target %s unsupported by "
331 "userspace tool", t->u.name);
332 memcpy(new->t, t, t->target_size +
333 sizeof(struct ebt_entry_target));
334
335 // I love pointers
336 **u_e = new;
337 *u_e = &new->next;
338 (*cnt)++;
339 (*totalcnt)++;
340 return 0;
341 } else { // a new chain
342 int i;
343 struct ebt_entries *entries = (struct ebt_entries *)e;
344 struct ebt_u_entries *new;
345
346 for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++)
347 if (valid_hooks & (1 << i))
348 break;
349 if (i >= NF_BR_NUMHOOKS)
350 print_bug("Not enough valid hooks");
351 *hook = i;
352 if (*n != *cnt)
353 print_bug("Nr of entries in the chain is wrong");
354 *n = entries->nentries;
355 *cnt = 0;
356 new = (struct ebt_u_entries *)
357 malloc(sizeof(struct ebt_u_entries));
358 if (!new)
359 print_memory();
360 new->nentries = entries->nentries;
361 new->policy = entries->policy;
362 new->entries = NULL;
363 u_repl->hook_entry[*hook] = new;
364 *u_e = &new->entries;
365 return 0;
366 }
367}
368
369// talk with kernel to receive the kernel's table
370void get_table(struct ebt_u_replace *u_repl)
371{
372 int i, j, k, hook;
373 socklen_t optlen;
374 struct ebt_replace repl;
375 struct ebt_u_entry **u_e;
376
377 get_sockfd();
378
379 optlen = sizeof(struct ebt_replace);
380 strcpy(repl.name, u_repl->name);
381 if (getsockopt(sockfd, IPPROTO_IP, EBT_SO_GET_INFO, &repl, &optlen))
382 print_error("A kernel module needed by your command is probably"
383 " not loaded. Try insmod ebtables or"
384 " insmod ebtable_%s", repl.name);
385
386 if ( !(repl.entries = (char *) malloc(repl.entries_size)) )
387 print_memory();
388 if (repl.nentries) {
389 if (!(repl.counters = (struct ebt_counter *)
390 malloc(repl.nentries * sizeof(struct ebt_counter))) )
391 print_memory();
392 }
393 else
394 repl.counters = NULL;
395
396 // we want to receive the counters
397 repl.num_counters = repl.nentries;
398 optlen += repl.entries_size + repl.num_counters *
399 sizeof(struct ebt_counter);
400 if (getsockopt(sockfd, IPPROTO_IP, EBT_SO_GET_ENTRIES, &repl, &optlen))
401 print_bug("hmm, what is wrong??? bug#1");
402
403 // translate the struct ebt_replace to a struct ebt_u_replace
404 memcpy(u_repl->name, repl.name, sizeof(u_repl->name));
405 u_repl->valid_hooks = repl.valid_hooks;
406 u_repl->nentries = repl.nentries;
407 u_repl->num_counters = repl.num_counters;
408 u_repl->counters = repl.counters;
409 memcpy(u_repl->counter_entry, repl.counter_entry,
410 sizeof(repl.counter_entry));
411 hook = -1;
412 i = 0; // holds the expected nr. of entries for the chain
413 j = 0; // holds the up to now counted entries for the chain
414 k = 0; // holds the total nr. of entries,
415 // should equal u_repl->nentries afterwards
416 EBT_ENTRY_ITERATE(repl.entries, repl.entries_size, ebt_translate_entry,
417 &hook, &i, &j, &k, &u_e, u_repl, u_repl->valid_hooks);
418 if (k != u_repl->nentries)
419 print_bug("Wrong total nentries");
420}
421
422void get_dbinfo(struct brdb_dbinfo *nr)
423{
424 socklen_t optlen = sizeof(struct brdb_dbinfo);
425
426 get_sockfd();
427
428 if (getsockopt(sockfd, IPPROTO_IP, BRDB_SO_GET_DBINFO, nr, &optlen))
429 print_error("Sorry, br_db code probably not in kernel, "
430 "try insmod br_db");
431}
432
433void get_db(int len, struct brdb_dbentry *db)
434{
435 socklen_t optlen = len;
436
437 get_sockfd();
438
439 if ( getsockopt(sockfd, IPPROTO_IP, BRDB_SO_GET_DB, db, &optlen) ) {
440 print_bug("hmm, what is wrong??? bug#2");
441 }
442}
443
444void deliver_allowdb(__u16 *decision)
445{
446 socklen_t optlen = sizeof(__u16);
447
448 get_sockfd();
449
450 if (setsockopt(sockfd, IPPROTO_IP, BRDB_SO_SET_ALLOWDB,
451 decision, optlen))
452 print_error("Sorry, br_db code probably not in kernel, "
453 "try insmod br_db");
454}