summaryrefslogtreecommitdiffstats
path: root/docs/ebtables-hacking/ebtables-hacking-HOWTO-4.html
blob: 39f7ac73363bb5e419b3cc1285d02f756058b744 (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
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>Ebtables Hacking HOWTO: Examples</title>
</head>
<body>
<a HREF="ebtables-hacking-HOWTO-3.html">Previous</a>
<a HREF="ebtables-hacking-HOWTO.html#toc4">Contents</a>
<hr>
<h2><a NAME="s4">4.</a> <a HREF="netfilter-hacking-HOWTO.html#toc4">Example</a></h2>
<p>
This section contains annotated example source code. This is just to give some clues, see the full code of the already implemented ebtables modules
for more clues.
</p>
<h2><a NAME="ss4.1">4.1</a> <a HREF="ebtables-hacking-HOWTO.html#toc4.1">Userspace</a></h2></p>
<p>
<em><h3>The userspace ip match module</h3></em>
</p>
<p>
What follows is annotated code of pieces of the <CODE>ebt_ip.c</CODE> code. For brevity, only the <CODE>--ip-source</CODE> option is considered.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
#include &lt;stdio.h&gt;
#include &lt;getopt.h&gt;
#include "../include/ebtables_u.h"
#include &lt;linux/netfilter_bridge/ebt_ip.h&gt;
</PRE>
</td></tr>
</table>
</p>
<p>
At least these includes are needed: respectively for <CODE>printf()</CODE>, for the parsing of the options, for all the
general ebtables userspace definitions and for the ebtables ip module specific header.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
#define IP_SOURCE '1'
#define IP_DEST   '2'

static struct option opts[] =
{
	{ "ip-source"     , required_argument, 0, IP_SOURCE },
	{ "ip-src"        , required_argument, 0, IP_SOURCE },
	{ 0 }
};
</PRE>
</td></tr>
</table>
</p>
<p>
Defining the new command line options. This is a struct that the library function <CODE>getopt_long()</CODE>
understands. The first field specifies the full length option name, the second field specifies if an argument is required
or not, the third field should be zero and the last field specifies the value returned by <CODE>getopt_long()</CODE> when
it encounters this option.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static void print_help()
{
	printf(
"ip options:\n"
"--ip-src    [!] address[/mask]: ip source specification\n"
}
</PRE>
</td></tr>
</table>
</p>
<p>
Short and descriptive help.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static void init(struct ebt_entry_match *match)
{
	struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;

	ipinfo->invflags = 0;
	ipinfo->bitmask = 0;
}
</PRE>
</td></tr>
</table>
</p>
<p>
Initialize the module specific data. In the example, some fields in the struct specific to the ip match module are given an initial value.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
#define OPT_SOURCE 0x01
static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
   unsigned int *flags, struct ebt_entry_match **match)
{
	struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)(*match)->data;
	char *end;
	long int i;

	switch (c) {
	case IP_SOURCE:
		check_option(flags, OPT_SOURCE);
		ipinfo->bitmask |= EBT_IP_SOURCE;

		if (check_inverse(optarg))
			ipinfo->invflags |= EBT_IP_SOURCE;

		if (optind > argc)
			print_error("Missing IP address argument");
		parse_ip_address(argv[optind - 1], &ipinfo->saddr, &ipinfo->smsk);
		break;

	default:
		return 0;
	}
	return 1;
}
</PRE>
</td></tr>
</table>
</p>
<p>
This function parses the option, filling in the match specific struct or exiting on input errors. <CODE>check_option()</CODE> makes sure the option isn't
specified twice on the command line. <CODE>check_inverse()</CODE> checks if the argument equals '!'. <CODE>optind &gt; argc</CODE> is possible if the command
line input ends with '--ip-source !'.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static void final_check(const struct ebt_u_entry *entry,
   const struct ebt_entry_match *match, const char *name,
   unsigned int hookmask, unsigned int time)
{
	if (entry->ethproto != ETH_P_IP || entry->invflags & EBT_IPROTO)
		print_error("For IP filtering the protocol must be "
		            "specified as IPv4");
}
</PRE>
</td></tr>
</table>
</p>
<p>
This function is executed for a rule that uses the (ip) module. When there is an error, it exits using <CODE>print_error()</CODE>.
<CODE>entry->ethproto</CODE> contains the protocol specified by the user. If the protocol wasn't specified, this field will equal zero. The specified protocol is
in host endian form, so there is no need for <CODE>ntohs()</CODE>.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static void print(const struct ebt_u_entry *entry,
   const struct ebt_entry_match *match)
{
	struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;
	int j;

	if (ipinfo->bitmask & EBT_IP_SOURCE) {
		printf("--ip-src ");
		if (ipinfo->invflags & EBT_IP_SOURCE)
			printf("! ");
		for (j = 0; j < 4; j++)
			printf("%d%s",((unsigned char *)&ipinfo->saddr)[j],
			   (j == 3) ? "" : ".");
		printf("%s ", mask_to_dotted(ipinfo->smsk));
	}
}
</PRE>
</td></tr>
</table>
</p>
<p>
Print out the data contained in the match in a form that the user could have used on the command line. End with at least one space.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static int compare(const struct ebt_entry_match *m1,
   const struct ebt_entry_match *m2)
{
	struct ebt_ip_info *ipinfo1 = (struct ebt_ip_info *)m1->data;
	struct ebt_ip_info *ipinfo2 = (struct ebt_ip_info *)m2->data;

	if (ipinfo1->bitmask != ipinfo2->bitmask)
		return 0;
	if (ipinfo1->invflags != ipinfo2->invflags)
		return 0;
	if (ipinfo1->bitmask & EBT_IP_SOURCE) {
		if (ipinfo1->saddr != ipinfo2->saddr)
			return 0;
		if (ipinfo1->smsk != ipinfo2->smsk)
			return 0;
	}
	return 1;
}
</PRE>
</td></tr>
</table>
</p>
<p>
Compares the data of two "instances" of the match module, returning 1 if the data is equivalent, else 0.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static struct ebt_u_match ip_match =
{
	.name		= EBT_IP_MATCH,
	.size		= sizeof(struct ebt_ip_info),
	.help		= print_help,
	.init		= init,
	.parse		= parse,
	.final_check	= final_check,
	.print		= print,
	.compare	= compare,
	.extra_ops	= opts,
};
</PRE>
</td></tr>
</table>
</p>
<p>
The struct used to register the match to the core ebtables userspace code.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static void _init(void) __attribute((constructor));
static void _init(void)
{
	register_match(&ip_match);
}
</PRE>
</td></tr>
</table>
</p>
<p>
The init function registers the match. Initializing its own data should be done with the <CODE>init()</CODE> function defined int the above struct.
</p>
<p>
<em><h3>The (userspace) dnat <CODE>final_check()</CODE> function</h3></em>
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static void final_check_d(const struct ebt_u_entry *entry,
   const struct ebt_entry_target *target, const char *name,
   unsigned int hookmask, unsigned int time)
{
	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;

	if (BASE_CHAIN && natinfo->target == EBT_RETURN)
		print_error("--dnat-target RETURN not allowed on base chain");
	CLEAR_BASE_CHAIN_BIT;
	if (((hookmask & ~((1 &lt;&lt; NF_BR_PRE_ROUTING) | (1 &lt;&lt; NF_BR_LOCAL_OUT)))
	   || strcmp(name, "nat")) &&
	   ((hookmask & ~(1 &lt;&lt; NF_BR_BROUTING)) || strcmp(name, "broute")))
		print_error("Wrong chain for dnat");
	if (time == 0 && to_dest_supplied == 0)
		print_error("No dnat address supplied");
}
</PRE>
</td></tr>
</table>
</p>
<p>
First we check that this target isn't RETURN on one of the standard (base) chains. Then we make
<CODE>hookmask</CODE> ready for direct use by using the <CODE>CLEAR_BASE_CHAIN_BIT</CODE> macro. Next is checked if the rule containing this
"module instance" is accessible through illegal chains or tables.
Finally, the argument <CODE>time</CODE> is checked. If it equals zero, the function checks to be sure a destination IP address was specified.
<CODE>to_dest_supplied</CODE> is a static variable of this ebtables userspace module, initialized to zero by its initialization function.
</p>
</h2>
<h2><a NAME="ss4.2">4.2</a> <a HREF="ebtables-hacking-HOWTO.html#toc4.2">Kernel</a>
</h2>
<p>
<em><h3>The ebtables kernel ip match module</h3></em>
</p>
<p>
What follows is annotated code of pieces of the <CODE>ebt_ip.c</CODE> code. For brevity, only the IP source address filtering is considered.
As the interface towards the main ebtables code is easy, there is really not much to be said here.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
#include &lt;linux/netfilter_bridge/ebtables.h&gt;
#include &lt;linux/netfilter_bridge/ebt_ip.h&gt;
#include &lt;linux/ip.h&gt;
#include &lt;linux/module.h&gt;
</PRE>
</td></tr>
</table>
</p>
<p>
We need general ebtables definitions, the ip kernel match module's specific data, the definition of the IP header and some module definitions.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
   const struct net_device *out, const void *data,
   unsigned int datalen)
{
	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
	union {struct iphdr iph; struct tcpudphdr ports;} u;

	if (skb_copy_bits(skb, 0, &u.iph, sizeof(u.iph)))
		return EBT_NOMATCH;
	if (info->bitmask & EBT_IP_SOURCE &&
	   FWINV((u.iph.saddr & info->smsk) !=
	   info->saddr, EBT_IP_SOURCE))
		return EBT_NOMATCH;
}
</PRE>
</td></tr>
</table>
</p>
<p>
This is the filtering function of the ip match module, it is executed for every
frame that comes into contact with an ebtables rule that uses the ip match. All it does
is tell the ebtables main code if the frame matches or not.<br>
As the data isn't necessarily linearized in memory, meaning that the data isn't
guaranteed to be in consecutive memory places, we need to use skb_copy_bits()
to copy the IP header to the stack. We can then match the data on the stack.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static int ebt_ip_check(const char *tablename, unsigned int hookmask,
   const struct ebt_entry *e, void *data, unsigned int datalen)
{
	struct ebt_ip_info *info = (struct ebt_ip_info *)data;

	if (datalen != sizeof(struct ebt_ip_info))
		return -EINVAL;
	if (e->ethproto != __constant_htons(ETH_P_IP) ||
	   e->invflags & EBT_IPROTO)
		return -EINVAL;
	if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
		return -EINVAL;
	return 0;
}
</PRE>
</td></tr>
</table>
</p>
<p>
This function is executed for every rule that uses the ip match, when the kernel receives new table data. It needs to make sure no corrupt ip match data is accepted.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static struct ebt_match filter_ip =
{
	.name		= EBT_IP_MATCH,
	.match		= ebt_filter_ip,
	.check		= ebt_ip_check,
	.me		= THIS_MODULE,
};
</PRE>
</td></tr>
</table>
</p>
<p>
The struct we'll give to the main ebtables code to register the match.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static int __init init(void)
{
	return ebt_register_match(&filter_ip);
}

static void __exit fini(void)
{
	ebt_unregister_match(&filter_ip);
}

module_init(init);
module_exit(fini);
</PRE>
</td></tr>
</table>
</p>
<p>
Functions executed at loading or removing of the ip match module.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
MODULE_LICENSE("GPL");
</PRE>
</td></tr>
</table>
</p>
<p>
Of course your module is released under the GPL.
</p>
<p>
<em><h3>The ebtables kernel filter table</h3></em>
</p>
<p>
This part contains pieces of the <CODE>ebtable_filter.c</CODE> code with annotation.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
#define FILTER_VALID_HOOKS ((1 &lt;&lt; NF_BR_LOCAL_IN) | (1 &lt;&lt; NF_BR_FORWARD) | \
   (1 &lt;&lt; NF_BR_LOCAL_OUT))

</PRE>
</td></tr>
</table>
</p>
<p>
The valid netfilter hooks for the ebtables filter table are the bridge <CODE>LOCAL_IN</CODE> <CODE>FORWARD</CODE> and <CODE>LOCAL_OUT</CODE> hooks.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static struct ebt_entries initial_chains[] =
{
	{
		.name	= "INPUT",
		.policy	= EBT_ACCEPT,
	},
	{
		.name	= "FORWARD",
		.policy	= EBT_ACCEPT,
	},
	{
		.name	= "OUTPUT",
		.policy	= EBT_ACCEPT,
	},
};

</PRE>
</td></tr>
</table>
</p>
<p>
The filter table consists of three chains, initially containing zero rules and having policy <CODE>ACCEPT</CODE>.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static struct ebt_replace initial_table =
{
	.name		= "filter",
	.valid_hooks	= FILTER_VALID_HOOKS,
	.entries_size	= 3 * sizeof(struct ebt_entries),
	.hook_entry	= {
		[NF_BR_LOCAL_IN]	= &initial_chains[0],
		[NF_BR_FORWARD]		= &initial_chains[1],
		[NF_BR_LOCAL_OUT]	= &initial_chains[2],
	},
	.entries	= (char *)initial_chains,
};

</PRE>
</td></tr>
</table>
</p>
<p>
This contains all the info about the table.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
{
	if (valid_hooks & ~FILTER_VALID_HOOKS)
		return -EINVAL;
	return 0;
}

</PRE>
</td></tr>
</table>
</p>
<p>
This function is executed when new table data is given to the kernel. We just check that
the valid hooks according to userspace are the same as those according to the kernel module.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static struct ebt_table frame_filter =
{ 
	.name		= "filter",
	.table		= &initial_table,
	.valid_hooks	= FILTER_VALID_HOOKS, 
	.lock		= RW_LOCK_UNLOCKED,
	.check		= check,
	.me		= THIS_MODULE,
};

</PRE>
</td></tr>
</table>
</p>
<p>
The ebtables main code likes to use this struct.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static unsigned int
ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
   const struct net_device *out, int (*okfn)(struct sk_buff *))
{
	return ebt_do_table(hook, pskb, in, out, &frame_filter);
}

</PRE>
</td></tr>
</table>
</p>
<p>
This function is executed for every frame that passes through a netfilter hook on which this function is registered.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static struct nf_hook_ops ebt_ops_filter[] = {
	{
		.hook		= ebt_hook,
		.owner		= THIS_MODULE,
		.pf		= PF_BRIDGE,
		.hooknum	= NF_BR_LOCAL_IN,
		.priority	= NF_BR_PRI_FILTER_BRIDGED,
	},
	{
		.hook		= ebt_hook,
		.owner		= THIS_MODULE,
		.pf		= PF_BRIDGE,
		.hooknum	= NF_BR_FORWARD,
		.priority	= NF_BR_PRI_FILTER_BRIDGED,
	},
	{
		.hook		= ebt_hook,
		.owner		= THIS_MODULE,
		.pf		= PF_BRIDGE,
		.hooknum	= NF_BR_LOCAL_OUT,
		.priority	= NF_BR_PRI_FILTER_OTHER,
	},
};

</PRE>
</td></tr>
</table>
</p>
<p>
Here we say the function <CODE>ebt_hook()</CODE> is registered onto the three mentioned netfilter hooks, with a certain priority (e.g. NF_BR_PRI_FILTER_BRIDGED).
If multiple functions are registered on the same hook, the priority of the functions determines the order in which they are executed. The lower the priority value,
the earlier the function will get executed.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static int __init init(void)
{
	int i, j, ret;

	ret = ebt_register_table(&frame_filter);
	if (ret &lt; 0)
		return ret;
	for (i = 0; i &lt; ARRAY_SIZE(ebt_ops_filter); i++)
		if ((ret = nf_register_hook(&ebt_ops_filter[i])) &lt; 0)
			goto cleanup;
	return ret;
cleanup:
	for (j = 0; j &lt; i; j++)
		nf_unregister_hook(&ebt_ops_filter[j]);
	ebt_unregister_table(&frame_filter);
	return ret;
}

</PRE>
</td></tr>
</table>
</p>
<p>
Register the table to the main ebtables code; register <CODE>ebt_hook()</CODE> on the appropriate netfilter hooks.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static void __exit fini(void)
{
	int i;

	for (i = 0; i &lt; ARRAY_SIZE(ebt_ops_filter); i++)
		nf_unregister_hook(&ebt_ops_filter[i]);
	ebt_unregister_table(&frame_filter);
}
</PRE>
</td></tr>
</table>
</p>
<p>
Unregister from the netfilter hooks and ebtables.
</p>
<hr>
Next
<a HREF="ebtables-hacking-HOWTO-3.html">Previous</a>
<a HREF="ebtables-hacking-HOWTO.html#toc4">Contents</a>
</body>
</html>