blob: 916d7af6a28aac163ffeb5bd27bd059f714b2da2 [file] [log] [blame]
Bart De Schuymer4883ba52002-09-19 21:10:45 +00001/*
2 * ebtables ebt_ip: IP extension module for userspace
3 *
4 * Authors:
5 * Bart De Schuymer <bart.de.schuymer@pandora.be>
6 *
7 * Changes:
8 * added ip-sport and ip-dport; parsing of port arguments is
9 * based on code from iptables-1.2.7a
10 * Innominate Security Technologies AG <mhopf@innominate.com>
11 * September, 2002
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 *
27 */
28
Bart De Schuymer1abc55d2002-06-01 19:23:47 +000029#include <stdio.h>
30#include <stdlib.h>
Bart De Schuymer1abc55d2002-06-01 19:23:47 +000031#include <string.h>
Bart De Schuymer1abc55d2002-06-01 19:23:47 +000032#include <getopt.h>
Bart De Schuymer4883ba52002-09-19 21:10:45 +000033#include <netdb.h>
Bart De Schuymer1abc55d2002-06-01 19:23:47 +000034#include "../include/ebtables_u.h"
35#include <linux/netfilter_bridge/ebt_ip.h>
36
37#define IP_SOURCE '1'
38#define IP_DEST '2'
39#define IP_myTOS '3' // include/bits/in.h seems to already define IP_TOS
40#define IP_PROTO '4'
Bart De Schuymer4883ba52002-09-19 21:10:45 +000041#define IP_SPORT '5'
42#define IP_DPORT '6'
Bart De Schuymer1abc55d2002-06-01 19:23:47 +000043
44static struct option opts[] =
45{
Bart De Schuymer4883ba52002-09-19 21:10:45 +000046 { "ip-source" , required_argument, 0, IP_SOURCE },
47 { "ip-src" , required_argument, 0, IP_SOURCE },
48 { "ip-destination" , required_argument, 0, IP_DEST },
49 { "ip-dst" , required_argument, 0, IP_DEST },
50 { "ip-tos" , required_argument, 0, IP_myTOS },
51 { "ip-protocol" , required_argument, 0, IP_PROTO },
52 { "ip-proto" , required_argument, 0, IP_PROTO },
53 { "ip-source-port" , required_argument, 0, IP_SPORT },
54 { "ip-sport" , required_argument, 0, IP_SPORT },
55 { "ip-destination-port" , required_argument, 0, IP_DPORT },
56 { "ip-dport" , required_argument, 0, IP_DPORT },
Bart De Schuymer1abc55d2002-06-01 19:23:47 +000057 { 0 }
58};
59
fnm3e1b09c32002-12-11 10:52:18 +000060struct protoent *pe;
61
Bart De Schuymer1abc55d2002-06-01 19:23:47 +000062// put the ip string into 4 bytes
63static int undot_ip(char *ip, unsigned char *ip2)
64{
65 char *p, *q, *end;
Bart De Schuymer9cfd6542002-08-13 16:08:08 +000066 long int onebyte;
67 int i;
Bart De Schuymer1abc55d2002-06-01 19:23:47 +000068 char buf[20];
69
70 strncpy(buf, ip, sizeof(buf) - 1);
71
72 p = buf;
73 for (i = 0; i < 3; i++) {
74 if ((q = strchr(p, '.')) == NULL)
75 return -1;
76 *q = '\0';
77 onebyte = strtol(p, &end, 10);
78 if (*end != '\0' || onebyte > 255 || onebyte < 0)
79 return -1;
80 ip2[i] = (unsigned char)onebyte;
81 p = q + 1;
82 }
83
84 onebyte = strtol(p, &end, 10);
Bart De Schuymer9cfd6542002-08-13 16:08:08 +000085 if (*end != '\0' || onebyte > 255 || onebyte < 0)
Bart De Schuymer1abc55d2002-06-01 19:23:47 +000086 return -1;
87 ip2[3] = (unsigned char)onebyte;
88
89 return 0;
90}
91
92// put the mask into 4 bytes
93static int ip_mask(char *mask, unsigned char *mask2)
94{
95 char *end;
Bart De Schuymer9cfd6542002-08-13 16:08:08 +000096 long int bits;
97 uint32_t mask22;
Bart De Schuymer1abc55d2002-06-01 19:23:47 +000098
99 if (undot_ip(mask, mask2)) {
100 // not the /a.b.c.e format, maybe the /x format
101 bits = strtol(mask, &end, 10);
102 if (*end != '\0' || bits > 32 || bits < 0)
103 return -1;
104 if (bits != 0) {
105 mask22 = htonl(0xFFFFFFFF << (32 - bits));
106 memcpy(mask2, &mask22, 4);
107 } else {
108 mask22 = 0xFFFFFFFF;
109 memcpy(mask2, &mask22, 4);
110 }
111 }
112 return 0;
113}
114
115// set the ip mask and ip address
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000116void parse_ip_address(char *address, uint32_t *addr, uint32_t *msk)
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000117{
118 char *p;
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000119
120 // first the mask
121 if ((p = strrchr(address, '/')) != NULL) {
122 *p = '\0';
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000123 if (ip_mask(p + 1, (unsigned char *)msk))
Bart De Schuymer23f6dcf2002-08-17 09:14:07 +0000124 print_error("Problem with the IP mask");
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000125 }
126 else
127 *msk = 0xFFFFFFFF;
128
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000129 if (undot_ip(address, (unsigned char *)addr))
Bart De Schuymer23f6dcf2002-08-17 09:14:07 +0000130 print_error("Problem with the IP address");
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000131 *addr = *addr & *msk;
132}
133
134// transform the ip mask into a string ready for output
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000135char *mask_to_dotted(uint32_t mask)
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000136{
137 int i;
138 static char buf[20];
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000139 uint32_t maskaddr, bits;
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000140
141 maskaddr = ntohl(mask);
142
143 // don't print /32
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000144 if (mask == 0xFFFFFFFFL) {
145 *buf = '\0';
146 return buf;
147 }
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000148
149 i = 32;
150 bits = 0xFFFFFFFEL; // case 0xFFFFFFFF has just been dealt with
151 while (--i >= 0 && maskaddr != bits)
152 bits <<= 1;
153
154 if (i > 0)
155 sprintf(buf, "/%d", i);
156 else if (!i)
157 *buf = '\0';
158 else
159 // mask was not a decent combination of 1's and 0's
160 sprintf(buf, "/%d.%d.%d.%d", ((unsigned char *)&mask)[0],
161 ((unsigned char *)&mask)[1], ((unsigned char *)&mask)[2],
162 ((unsigned char *)&mask)[3]);
163
164 return buf;
165}
166
Bart De Schuymer4883ba52002-09-19 21:10:45 +0000167// transform a protocol and service name into a port number
168static uint16_t parse_port(const char *protocol, const char *name)
169{
170 struct servent *service;
171 char *end;
172 int port;
173
174 port = strtol(name, &end, 10);
175 if (*end != '\0') {
176 if (protocol &&
177 (service = getservbyname(name, protocol)) != NULL)
178 return ntohs(service->s_port);
179 }
180 else if (port >= 0 || port <= 0xFFFF) {
181 return port;
182 }
183 print_error("Problem with specified %s port '%s'",
184 protocol?protocol:"", name);
185 return 0; /* never reached */
186}
187
188static void
189parse_port_range(const char *protocol, const char *portstring, uint16_t *ports)
190{
191 char *buffer;
192 char *cp;
193
194 buffer = strdup(portstring);
195 if ((cp = strchr(buffer, ':')) == NULL)
196 ports[0] = ports[1] = parse_port(protocol, buffer);
197 else {
198 *cp = '\0';
199 cp++;
200 ports[0] = buffer[0] ? parse_port(protocol, buffer) : 0;
201 ports[1] = cp[0] ? parse_port(protocol, cp) : 0xFFFF;
202
203 if (ports[0] > ports[1])
204 print_error("Invalid portrange (min > max)");
205 }
206 free(buffer);
207}
208
209static void print_port_range(uint16_t *ports)
210{
211 if (ports[0] == ports[1])
Bart De Schuymer2a9b5882002-10-11 18:29:00 +0000212 printf("%d ", ports[0]);
Bart De Schuymer4883ba52002-09-19 21:10:45 +0000213 else
Bart De Schuymer2a9b5882002-10-11 18:29:00 +0000214 printf("%d:%d ", ports[0], ports[1]);
Bart De Schuymer4883ba52002-09-19 21:10:45 +0000215}
216
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000217static void print_help()
218{
219 printf(
220"ip options:\n"
221"--ip-src [!] address[/mask]: ip source specification\n"
222"--ip-dst [!] address[/mask]: ip destination specification\n"
223"--ip-tos [!] tos : ip tos specification\n"
Bart De Schuymer4883ba52002-09-19 21:10:45 +0000224"--ip-proto [!] protocol : ip protocol specification\n"
225"--ip-sport [!] port[:port] : tcp/udp source port or port range\n"
226"--ip-dport [!] port[:port] : tcp/udp destination port or port range\n");
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000227}
228
229static void init(struct ebt_entry_match *match)
230{
231 struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;
232
233 ipinfo->invflags = 0;
234 ipinfo->bitmask = 0;
235}
236
237#define OPT_SOURCE 0x01
238#define OPT_DEST 0x02
239#define OPT_TOS 0x04
240#define OPT_PROTO 0x08
Bart De Schuymer4883ba52002-09-19 21:10:45 +0000241#define OPT_SPORT 0x10
242#define OPT_DPORT 0x20
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000243static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
244 unsigned int *flags, struct ebt_entry_match **match)
245{
246 struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)(*match)->data;
Bart De Schuymerf8f8f292002-06-25 15:43:57 +0000247 char *end;
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000248 long int i;
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000249
250 switch (c) {
251 case IP_SOURCE:
252 check_option(flags, OPT_SOURCE);
253 ipinfo->bitmask |= EBT_IP_SOURCE;
254
255 case IP_DEST:
256 if (c == IP_DEST) {
257 check_option(flags, OPT_DEST);
258 ipinfo->bitmask |= EBT_IP_DEST;
259 }
260 if (check_inverse(optarg)) {
261 if (c == IP_SOURCE)
262 ipinfo->invflags |= EBT_IP_SOURCE;
263 else
264 ipinfo->invflags |= EBT_IP_DEST;
265 }
266
267 if (optind > argc)
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000268 print_error("Missing IP address argument");
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000269 if (c == IP_SOURCE)
270 parse_ip_address(argv[optind - 1], &ipinfo->saddr,
271 &ipinfo->smsk);
272 else
273 parse_ip_address(argv[optind - 1], &ipinfo->daddr,
274 &ipinfo->dmsk);
275 break;
276
Bart De Schuymer4883ba52002-09-19 21:10:45 +0000277 case IP_SPORT:
278 case IP_DPORT:
279 if (c == IP_SPORT) {
280 check_option(flags, OPT_SPORT);
281 ipinfo->bitmask |= EBT_IP_SPORT;
282 if (check_inverse(optarg))
283 ipinfo->invflags |= EBT_IP_SPORT;
284 } else {
285 check_option(flags, OPT_DPORT);
286 ipinfo->bitmask |= EBT_IP_DPORT;
287 if (check_inverse(optarg))
288 ipinfo->invflags |= EBT_IP_DPORT;
289 }
290 if (optind > argc)
291 print_error("Missing port argument");
292 if (c == IP_SPORT)
293 parse_port_range(NULL, argv[optind - 1], ipinfo->sport);
294 else
295 parse_port_range(NULL, argv[optind - 1], ipinfo->dport);
296 break;
297
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000298 case IP_myTOS:
299 check_option(flags, OPT_TOS);
300 if (check_inverse(optarg))
301 ipinfo->invflags |= EBT_IP_TOS;
302
303 if (optind > argc)
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000304 print_error("Missing IP tos argument");
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000305 i = strtol(argv[optind - 1], &end, 16);
Bart De Schuymerf8f8f292002-06-25 15:43:57 +0000306 if (i < 0 || i > 255 || *end != '\0')
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000307 print_error("Problem with specified IP tos");
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000308 ipinfo->tos = i;
309 ipinfo->bitmask |= EBT_IP_TOS;
310 break;
311
312 case IP_PROTO:
313 check_option(flags, OPT_PROTO);
314 if (check_inverse(optarg))
315 ipinfo->invflags |= EBT_IP_PROTO;
316 if (optind > argc)
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000317 print_error("Missing IP protocol argument");
fnm3e1b09c32002-12-11 10:52:18 +0000318 (unsigned char) i = strtoul(argv[optind - 1], &end, 10);
319 if (*end != '\0') {
320 pe = getprotobyname(argv[optind - 1]);
321 if (pe == NULL)
322 print_error
323 ("Unknown specified IP protocol - %s",
324 argv[optind - 1]);
325 ipinfo->protocol = pe->p_proto;
326 } else {
327 ipinfo->protocol = (unsigned char) i;
328 }
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000329 ipinfo->bitmask |= EBT_IP_PROTO;
330 break;
331 default:
332 return 0;
333 }
334 return 1;
335}
336
337static void final_check(const struct ebt_u_entry *entry,
Bart De Schuymer7b9aaeb2002-06-23 20:38:34 +0000338 const struct ebt_entry_match *match, const char *name,
Bart De Schuymerc9b52932002-08-24 13:26:34 +0000339 unsigned int hookmask, unsigned int time)
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000340{
Bart De Schuymer4883ba52002-09-19 21:10:45 +0000341 struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;
342
Bart De Schuymer40573192002-08-29 16:48:36 +0000343 if (entry->ethproto != ETH_P_IP || entry->invflags & EBT_IPROTO)
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000344 print_error("For IP filtering the protocol must be "
345 "specified as IPv4");
Bart De Schuymer4883ba52002-09-19 21:10:45 +0000346
347 if (ipinfo->bitmask & (EBT_IP_SPORT|EBT_IP_DPORT) &&
348 (!ipinfo->bitmask & EBT_IP_PROTO ||
349 ipinfo->invflags & EBT_IP_PROTO ||
350 (ipinfo->protocol!=IPPROTO_TCP &&
351 ipinfo->protocol!=IPPROTO_UDP)))
352 print_error("For port filtering the IP protocol must be "
353 "either 6 (tcp) or 17 (udp)");
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000354}
355
356static void print(const struct ebt_u_entry *entry,
357 const struct ebt_entry_match *match)
358{
359 struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;
360 int j;
361
362 if (ipinfo->bitmask & EBT_IP_SOURCE) {
Bart De Schuymer41e8a192002-06-23 08:03:12 +0000363 printf("--ip-src ");
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000364 if (ipinfo->invflags & EBT_IP_SOURCE)
365 printf("! ");
366 for (j = 0; j < 4; j++)
367 printf("%d%s",((unsigned char *)&ipinfo->saddr)[j],
368 (j == 3) ? "" : ".");
Bart De Schuymer41e8a192002-06-23 08:03:12 +0000369 printf("%s ", mask_to_dotted(ipinfo->smsk));
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000370 }
371 if (ipinfo->bitmask & EBT_IP_DEST) {
Bart De Schuymer41e8a192002-06-23 08:03:12 +0000372 printf("--ip-dst ");
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000373 if (ipinfo->invflags & EBT_IP_DEST)
374 printf("! ");
375 for (j = 0; j < 4; j++)
376 printf("%d%s", ((unsigned char *)&ipinfo->daddr)[j],
377 (j == 3) ? "" : ".");
Bart De Schuymer41e8a192002-06-23 08:03:12 +0000378 printf("%s ", mask_to_dotted(ipinfo->dmsk));
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000379 }
380 if (ipinfo->bitmask & EBT_IP_TOS) {
Bart De Schuymer41e8a192002-06-23 08:03:12 +0000381 printf("--ip-tos ");
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000382 if (ipinfo->invflags & EBT_IP_TOS)
383 printf("! ");
Bart De Schuymer41e8a192002-06-23 08:03:12 +0000384 printf("0x%02X ", ipinfo->tos);
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000385 }
386 if (ipinfo->bitmask & EBT_IP_PROTO) {
Bart De Schuymer41e8a192002-06-23 08:03:12 +0000387 printf("--ip-proto ");
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000388 if (ipinfo->invflags & EBT_IP_PROTO)
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000389 printf("! ");
fnm3e1b09c32002-12-11 10:52:18 +0000390 pe = getprotobynumber(ipinfo->protocol);
391 if (pe == NULL) {
392 printf("%d ", ipinfo->protocol);
393 } else {
394 printf("%s ", pe->p_name);
395 }
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000396 }
Bart De Schuymer4883ba52002-09-19 21:10:45 +0000397 if (ipinfo->bitmask & EBT_IP_SPORT) {
398 printf("--ip-sport ");
399 if (ipinfo->invflags & EBT_IP_SPORT) {
400 printf("! ");
401 }
402 print_port_range(ipinfo->sport);
403 }
404 if (ipinfo->bitmask & EBT_IP_DPORT) {
405 printf("--ip-dport ");
406 if (ipinfo->invflags & EBT_IP_DPORT) {
407 printf("! ");
408 }
409 print_port_range(ipinfo->dport);
410 }
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000411}
412
413static int compare(const struct ebt_entry_match *m1,
414 const struct ebt_entry_match *m2)
415{
416 struct ebt_ip_info *ipinfo1 = (struct ebt_ip_info *)m1->data;
417 struct ebt_ip_info *ipinfo2 = (struct ebt_ip_info *)m2->data;
418
419 if (ipinfo1->bitmask != ipinfo2->bitmask)
420 return 0;
421 if (ipinfo1->invflags != ipinfo2->invflags)
422 return 0;
423 if (ipinfo1->bitmask & EBT_IP_SOURCE) {
424 if (ipinfo1->saddr != ipinfo2->saddr)
425 return 0;
426 if (ipinfo1->smsk != ipinfo2->smsk)
427 return 0;
428 }
429 if (ipinfo1->bitmask & EBT_IP_DEST) {
430 if (ipinfo1->daddr != ipinfo2->daddr)
431 return 0;
432 if (ipinfo1->dmsk != ipinfo2->dmsk)
433 return 0;
434 }
435 if (ipinfo1->bitmask & EBT_IP_TOS) {
436 if (ipinfo1->tos != ipinfo2->tos)
437 return 0;
438 }
439 if (ipinfo1->bitmask & EBT_IP_PROTO) {
440 if (ipinfo1->protocol != ipinfo2->protocol)
441 return 0;
442 }
Bart De Schuymer4883ba52002-09-19 21:10:45 +0000443 if (ipinfo1->bitmask & EBT_IP_SPORT) {
444 if (ipinfo1->sport != ipinfo2->sport)
445 return 0;
446 }
447 if (ipinfo1->bitmask & EBT_IP_DPORT) {
448 if (ipinfo1->dport != ipinfo2->dport)
449 return 0;
450 }
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000451 return 1;
452}
453
454static struct ebt_u_match ip_match =
455{
456 EBT_IP_MATCH,
457 sizeof(struct ebt_ip_info),
458 print_help,
459 init,
460 parse,
461 final_check,
462 print,
463 compare,
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000464 opts
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000465};
466
467static void _init(void) __attribute((constructor));
468static void _init(void)
469{
470 register_match(&ip_match);
471}