blob: 1351b421829fe993aa57d1468e1063c3bd4052aa [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
60// put the ip string into 4 bytes
61static int undot_ip(char *ip, unsigned char *ip2)
62{
63 char *p, *q, *end;
Bart De Schuymer9cfd6542002-08-13 16:08:08 +000064 long int onebyte;
65 int i;
Bart De Schuymer1abc55d2002-06-01 19:23:47 +000066 char buf[20];
67
68 strncpy(buf, ip, sizeof(buf) - 1);
69
70 p = buf;
71 for (i = 0; i < 3; i++) {
72 if ((q = strchr(p, '.')) == NULL)
73 return -1;
74 *q = '\0';
75 onebyte = strtol(p, &end, 10);
76 if (*end != '\0' || onebyte > 255 || onebyte < 0)
77 return -1;
78 ip2[i] = (unsigned char)onebyte;
79 p = q + 1;
80 }
81
82 onebyte = strtol(p, &end, 10);
Bart De Schuymer9cfd6542002-08-13 16:08:08 +000083 if (*end != '\0' || onebyte > 255 || onebyte < 0)
Bart De Schuymer1abc55d2002-06-01 19:23:47 +000084 return -1;
85 ip2[3] = (unsigned char)onebyte;
86
87 return 0;
88}
89
90// put the mask into 4 bytes
91static int ip_mask(char *mask, unsigned char *mask2)
92{
93 char *end;
Bart De Schuymer9cfd6542002-08-13 16:08:08 +000094 long int bits;
95 uint32_t mask22;
Bart De Schuymer1abc55d2002-06-01 19:23:47 +000096
97 if (undot_ip(mask, mask2)) {
98 // not the /a.b.c.e format, maybe the /x format
99 bits = strtol(mask, &end, 10);
100 if (*end != '\0' || bits > 32 || bits < 0)
101 return -1;
102 if (bits != 0) {
103 mask22 = htonl(0xFFFFFFFF << (32 - bits));
104 memcpy(mask2, &mask22, 4);
105 } else {
106 mask22 = 0xFFFFFFFF;
107 memcpy(mask2, &mask22, 4);
108 }
109 }
110 return 0;
111}
112
113// set the ip mask and ip address
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000114void parse_ip_address(char *address, uint32_t *addr, uint32_t *msk)
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000115{
116 char *p;
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000117
118 // first the mask
119 if ((p = strrchr(address, '/')) != NULL) {
120 *p = '\0';
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000121 if (ip_mask(p + 1, (unsigned char *)msk))
Bart De Schuymer23f6dcf2002-08-17 09:14:07 +0000122 print_error("Problem with the IP mask");
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000123 }
124 else
125 *msk = 0xFFFFFFFF;
126
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000127 if (undot_ip(address, (unsigned char *)addr))
Bart De Schuymer23f6dcf2002-08-17 09:14:07 +0000128 print_error("Problem with the IP address");
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000129 *addr = *addr & *msk;
130}
131
132// transform the ip mask into a string ready for output
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000133char *mask_to_dotted(uint32_t mask)
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000134{
135 int i;
136 static char buf[20];
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000137 uint32_t maskaddr, bits;
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000138
139 maskaddr = ntohl(mask);
140
141 // don't print /32
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000142 if (mask == 0xFFFFFFFFL) {
143 *buf = '\0';
144 return buf;
145 }
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000146
147 i = 32;
148 bits = 0xFFFFFFFEL; // case 0xFFFFFFFF has just been dealt with
149 while (--i >= 0 && maskaddr != bits)
150 bits <<= 1;
151
152 if (i > 0)
153 sprintf(buf, "/%d", i);
154 else if (!i)
155 *buf = '\0';
156 else
157 // mask was not a decent combination of 1's and 0's
158 sprintf(buf, "/%d.%d.%d.%d", ((unsigned char *)&mask)[0],
159 ((unsigned char *)&mask)[1], ((unsigned char *)&mask)[2],
160 ((unsigned char *)&mask)[3]);
161
162 return buf;
163}
164
Bart De Schuymer4883ba52002-09-19 21:10:45 +0000165// transform a protocol and service name into a port number
166static uint16_t parse_port(const char *protocol, const char *name)
167{
168 struct servent *service;
169 char *end;
170 int port;
171
172 port = strtol(name, &end, 10);
173 if (*end != '\0') {
174 if (protocol &&
175 (service = getservbyname(name, protocol)) != NULL)
176 return ntohs(service->s_port);
177 }
178 else if (port >= 0 || port <= 0xFFFF) {
179 return port;
180 }
181 print_error("Problem with specified %s port '%s'",
182 protocol?protocol:"", name);
183 return 0; /* never reached */
184}
185
186static void
187parse_port_range(const char *protocol, const char *portstring, uint16_t *ports)
188{
189 char *buffer;
190 char *cp;
191
192 buffer = strdup(portstring);
193 if ((cp = strchr(buffer, ':')) == NULL)
194 ports[0] = ports[1] = parse_port(protocol, buffer);
195 else {
196 *cp = '\0';
197 cp++;
198 ports[0] = buffer[0] ? parse_port(protocol, buffer) : 0;
199 ports[1] = cp[0] ? parse_port(protocol, cp) : 0xFFFF;
200
201 if (ports[0] > ports[1])
202 print_error("Invalid portrange (min > max)");
203 }
204 free(buffer);
205}
206
207static void print_port_range(uint16_t *ports)
208{
209 if (ports[0] == ports[1])
210 printf("%d ", ntohs(ports[0]));
211 else
212 printf("%d:%d ", ntohs(ports[0]), ntohs(ports[1]));
213}
214
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000215static void print_help()
216{
217 printf(
218"ip options:\n"
219"--ip-src [!] address[/mask]: ip source specification\n"
220"--ip-dst [!] address[/mask]: ip destination specification\n"
221"--ip-tos [!] tos : ip tos specification\n"
Bart De Schuymer4883ba52002-09-19 21:10:45 +0000222"--ip-proto [!] protocol : ip protocol specification\n"
223"--ip-sport [!] port[:port] : tcp/udp source port or port range\n"
224"--ip-dport [!] port[:port] : tcp/udp destination port or port range\n");
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000225}
226
227static void init(struct ebt_entry_match *match)
228{
229 struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;
230
231 ipinfo->invflags = 0;
232 ipinfo->bitmask = 0;
233}
234
235#define OPT_SOURCE 0x01
236#define OPT_DEST 0x02
237#define OPT_TOS 0x04
238#define OPT_PROTO 0x08
Bart De Schuymer4883ba52002-09-19 21:10:45 +0000239#define OPT_SPORT 0x10
240#define OPT_DPORT 0x20
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000241static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
242 unsigned int *flags, struct ebt_entry_match **match)
243{
244 struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)(*match)->data;
Bart De Schuymerf8f8f292002-06-25 15:43:57 +0000245 char *end;
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000246 long int i;
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000247
248 switch (c) {
249 case IP_SOURCE:
250 check_option(flags, OPT_SOURCE);
251 ipinfo->bitmask |= EBT_IP_SOURCE;
252
253 case IP_DEST:
254 if (c == IP_DEST) {
255 check_option(flags, OPT_DEST);
256 ipinfo->bitmask |= EBT_IP_DEST;
257 }
258 if (check_inverse(optarg)) {
259 if (c == IP_SOURCE)
260 ipinfo->invflags |= EBT_IP_SOURCE;
261 else
262 ipinfo->invflags |= EBT_IP_DEST;
263 }
264
265 if (optind > argc)
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000266 print_error("Missing IP address argument");
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000267 if (c == IP_SOURCE)
268 parse_ip_address(argv[optind - 1], &ipinfo->saddr,
269 &ipinfo->smsk);
270 else
271 parse_ip_address(argv[optind - 1], &ipinfo->daddr,
272 &ipinfo->dmsk);
273 break;
274
Bart De Schuymer4883ba52002-09-19 21:10:45 +0000275 case IP_SPORT:
276 case IP_DPORT:
277 if (c == IP_SPORT) {
278 check_option(flags, OPT_SPORT);
279 ipinfo->bitmask |= EBT_IP_SPORT;
280 if (check_inverse(optarg))
281 ipinfo->invflags |= EBT_IP_SPORT;
282 } else {
283 check_option(flags, OPT_DPORT);
284 ipinfo->bitmask |= EBT_IP_DPORT;
285 if (check_inverse(optarg))
286 ipinfo->invflags |= EBT_IP_DPORT;
287 }
288 if (optind > argc)
289 print_error("Missing port argument");
290 if (c == IP_SPORT)
291 parse_port_range(NULL, argv[optind - 1], ipinfo->sport);
292 else
293 parse_port_range(NULL, argv[optind - 1], ipinfo->dport);
294 break;
295
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000296 case IP_myTOS:
297 check_option(flags, OPT_TOS);
298 if (check_inverse(optarg))
299 ipinfo->invflags |= EBT_IP_TOS;
300
301 if (optind > argc)
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000302 print_error("Missing IP tos argument");
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000303 i = strtol(argv[optind - 1], &end, 16);
Bart De Schuymerf8f8f292002-06-25 15:43:57 +0000304 if (i < 0 || i > 255 || *end != '\0')
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000305 print_error("Problem with specified IP tos");
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000306 ipinfo->tos = i;
307 ipinfo->bitmask |= EBT_IP_TOS;
308 break;
309
310 case IP_PROTO:
311 check_option(flags, OPT_PROTO);
312 if (check_inverse(optarg))
313 ipinfo->invflags |= EBT_IP_PROTO;
314 if (optind > argc)
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000315 print_error("Missing IP protocol argument");
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000316 i = strtol(argv[optind - 1], &end, 10);
317 if (i < 0 || i > 255 || *end != '\0')
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000318 print_error("Problem with specified IP protocol");
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000319 ipinfo->protocol = i;
320 ipinfo->bitmask |= EBT_IP_PROTO;
321 break;
322 default:
323 return 0;
324 }
325 return 1;
326}
327
328static void final_check(const struct ebt_u_entry *entry,
Bart De Schuymer7b9aaeb2002-06-23 20:38:34 +0000329 const struct ebt_entry_match *match, const char *name,
Bart De Schuymerc9b52932002-08-24 13:26:34 +0000330 unsigned int hookmask, unsigned int time)
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000331{
Bart De Schuymer4883ba52002-09-19 21:10:45 +0000332 struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;
333
Bart De Schuymer40573192002-08-29 16:48:36 +0000334 if (entry->ethproto != ETH_P_IP || entry->invflags & EBT_IPROTO)
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000335 print_error("For IP filtering the protocol must be "
336 "specified as IPv4");
Bart De Schuymer4883ba52002-09-19 21:10:45 +0000337
338 if (ipinfo->bitmask & (EBT_IP_SPORT|EBT_IP_DPORT) &&
339 (!ipinfo->bitmask & EBT_IP_PROTO ||
340 ipinfo->invflags & EBT_IP_PROTO ||
341 (ipinfo->protocol!=IPPROTO_TCP &&
342 ipinfo->protocol!=IPPROTO_UDP)))
343 print_error("For port filtering the IP protocol must be "
344 "either 6 (tcp) or 17 (udp)");
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000345}
346
347static void print(const struct ebt_u_entry *entry,
348 const struct ebt_entry_match *match)
349{
350 struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;
351 int j;
352
353 if (ipinfo->bitmask & EBT_IP_SOURCE) {
Bart De Schuymer41e8a192002-06-23 08:03:12 +0000354 printf("--ip-src ");
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000355 if (ipinfo->invflags & EBT_IP_SOURCE)
356 printf("! ");
357 for (j = 0; j < 4; j++)
358 printf("%d%s",((unsigned char *)&ipinfo->saddr)[j],
359 (j == 3) ? "" : ".");
Bart De Schuymer41e8a192002-06-23 08:03:12 +0000360 printf("%s ", mask_to_dotted(ipinfo->smsk));
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000361 }
362 if (ipinfo->bitmask & EBT_IP_DEST) {
Bart De Schuymer41e8a192002-06-23 08:03:12 +0000363 printf("--ip-dst ");
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000364 if (ipinfo->invflags & EBT_IP_DEST)
365 printf("! ");
366 for (j = 0; j < 4; j++)
367 printf("%d%s", ((unsigned char *)&ipinfo->daddr)[j],
368 (j == 3) ? "" : ".");
Bart De Schuymer41e8a192002-06-23 08:03:12 +0000369 printf("%s ", mask_to_dotted(ipinfo->dmsk));
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000370 }
371 if (ipinfo->bitmask & EBT_IP_TOS) {
Bart De Schuymer41e8a192002-06-23 08:03:12 +0000372 printf("--ip-tos ");
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000373 if (ipinfo->invflags & EBT_IP_TOS)
374 printf("! ");
Bart De Schuymer41e8a192002-06-23 08:03:12 +0000375 printf("0x%02X ", ipinfo->tos);
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000376 }
377 if (ipinfo->bitmask & EBT_IP_PROTO) {
Bart De Schuymer41e8a192002-06-23 08:03:12 +0000378 printf("--ip-proto ");
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000379 if (ipinfo->invflags & EBT_IP_PROTO)
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000380 printf("! ");
Bart De Schuymer41e8a192002-06-23 08:03:12 +0000381 printf("%d ", ipinfo->protocol);
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000382 }
Bart De Schuymer4883ba52002-09-19 21:10:45 +0000383 if (ipinfo->bitmask & EBT_IP_SPORT) {
384 printf("--ip-sport ");
385 if (ipinfo->invflags & EBT_IP_SPORT) {
386 printf("! ");
387 }
388 print_port_range(ipinfo->sport);
389 }
390 if (ipinfo->bitmask & EBT_IP_DPORT) {
391 printf("--ip-dport ");
392 if (ipinfo->invflags & EBT_IP_DPORT) {
393 printf("! ");
394 }
395 print_port_range(ipinfo->dport);
396 }
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000397}
398
399static int compare(const struct ebt_entry_match *m1,
400 const struct ebt_entry_match *m2)
401{
402 struct ebt_ip_info *ipinfo1 = (struct ebt_ip_info *)m1->data;
403 struct ebt_ip_info *ipinfo2 = (struct ebt_ip_info *)m2->data;
404
405 if (ipinfo1->bitmask != ipinfo2->bitmask)
406 return 0;
407 if (ipinfo1->invflags != ipinfo2->invflags)
408 return 0;
409 if (ipinfo1->bitmask & EBT_IP_SOURCE) {
410 if (ipinfo1->saddr != ipinfo2->saddr)
411 return 0;
412 if (ipinfo1->smsk != ipinfo2->smsk)
413 return 0;
414 }
415 if (ipinfo1->bitmask & EBT_IP_DEST) {
416 if (ipinfo1->daddr != ipinfo2->daddr)
417 return 0;
418 if (ipinfo1->dmsk != ipinfo2->dmsk)
419 return 0;
420 }
421 if (ipinfo1->bitmask & EBT_IP_TOS) {
422 if (ipinfo1->tos != ipinfo2->tos)
423 return 0;
424 }
425 if (ipinfo1->bitmask & EBT_IP_PROTO) {
426 if (ipinfo1->protocol != ipinfo2->protocol)
427 return 0;
428 }
Bart De Schuymer4883ba52002-09-19 21:10:45 +0000429 if (ipinfo1->bitmask & EBT_IP_SPORT) {
430 if (ipinfo1->sport != ipinfo2->sport)
431 return 0;
432 }
433 if (ipinfo1->bitmask & EBT_IP_DPORT) {
434 if (ipinfo1->dport != ipinfo2->dport)
435 return 0;
436 }
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000437 return 1;
438}
439
440static struct ebt_u_match ip_match =
441{
442 EBT_IP_MATCH,
443 sizeof(struct ebt_ip_info),
444 print_help,
445 init,
446 parse,
447 final_check,
448 print,
449 compare,
Bart De Schuymer9cfd6542002-08-13 16:08:08 +0000450 opts
Bart De Schuymer1abc55d2002-06-01 19:23:47 +0000451};
452
453static void _init(void) __attribute((constructor));
454static void _init(void)
455{
456 register_match(&ip_match);
457}