www.usr.com/support/gpl/USR9107_release.1.4.tar.gz
[bcm963xx.git] / userapps / opensource / ebtables / extensions / ebt_ip.c
1 /*
2  *  ebtables ebt_ip: IP extension module for userspace
3  * 
4  *  Authors:
5  *   Bart De Schuymer <bdschuym@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
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <getopt.h>
33 #include <netdb.h>
34 #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'
41 #define IP_SPORT  '5'
42 #define IP_DPORT  '6'
43
44 static struct option opts[] =
45 {
46         { "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  },
57         { 0 }
58 };
59
60 /* put the ip string into 4 bytes */
61 static int undot_ip(char *ip, unsigned char *ip2)
62 {
63         char *p, *q, *end;
64         long int onebyte;
65         int i;
66         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);
83         if (*end != '\0' || onebyte > 255 || onebyte < 0)
84                 return -1;
85         ip2[3] = (unsigned char)onebyte;
86
87         return 0;
88 }
89
90 /* put the mask into 4 bytes */
91 static int ip_mask(char *mask, unsigned char *mask2)
92 {
93         char *end;
94         long int bits;
95         uint32_t mask22;
96
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 */
114 void parse_ip_address(char *address, uint32_t *addr, uint32_t *msk)
115 {
116         char *p;
117
118         /* first the mask */
119         if ((p = strrchr(address, '/')) != NULL) {
120                 *p = '\0';
121                 if (ip_mask(p + 1, (unsigned char *)msk))
122                         print_error("Problem with the IP mask");
123         }
124         else
125                 *msk = 0xFFFFFFFF;
126
127         if (undot_ip(address, (unsigned char *)addr))
128                 print_error("Problem with the IP address");
129         *addr = *addr & *msk;
130 }
131
132 /* transform the ip mask into a string ready for output */
133 char *mask_to_dotted(uint32_t mask)
134 {
135         int i;
136         static char buf[20];
137         uint32_t maskaddr, bits;
138
139         maskaddr = ntohl(mask);
140
141         /* don't print /32 */
142         if (mask == 0xFFFFFFFFL) {
143                 *buf = '\0';
144                 return buf;
145         }
146
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
165 /* transform a protocol and service name into a port number */
166 static 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
186 static void
187 parse_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
207 static void print_port_range(uint16_t *ports)
208 {
209         if (ports[0] == ports[1])
210                 printf("%d ", ports[0]);
211         else
212                 printf("%d:%d ", ports[0], ports[1]);
213 }
214
215 static 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"
222 "--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");
225 }
226
227 static 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
239 #define OPT_SPORT  0x10
240 #define OPT_DPORT  0x20
241 static 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;
245         char *end;
246         long int i;
247
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)
266                         print_error("Missing IP address argument");
267                 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
275         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
296         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)
302                         print_error("Missing IP tos argument");
303                 i = strtol(argv[optind - 1], &end, 16);
304                 if (i < 0 || i > 255 || *end != '\0')
305                         print_error("Problem with specified IP tos");
306                 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)
315                         print_error("Missing IP protocol argument");
316                 (unsigned char) i = strtoul(argv[optind - 1], &end, 10);
317                 if (*end != '\0') {
318                         struct protoent *pe;
319
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                 }
329                 ipinfo->bitmask |= EBT_IP_PROTO;
330                 break;
331         default:
332                 return 0;
333         }
334         return 1;
335 }
336
337 static void final_check(const struct ebt_u_entry *entry,
338    const struct ebt_entry_match *match, const char *name,
339    unsigned int hookmask, unsigned int time)
340 {
341         struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;
342
343         if (entry->ethproto != ETH_P_IP || entry->invflags & EBT_IPROTO)
344                 print_error("For IP filtering the protocol must be "
345                             "specified as IPv4");
346
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)");
354 }
355
356 static 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) {
363                 printf("--ip-src ");
364                 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) ? "" : ".");
369                 printf("%s ", mask_to_dotted(ipinfo->smsk));
370         }
371         if (ipinfo->bitmask & EBT_IP_DEST) {
372                 printf("--ip-dst ");
373                 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) ? "" : ".");
378                 printf("%s ", mask_to_dotted(ipinfo->dmsk));
379         }
380         if (ipinfo->bitmask & EBT_IP_TOS) {
381                 printf("--ip-tos ");
382                 if (ipinfo->invflags & EBT_IP_TOS)
383                         printf("! ");
384                 printf("0x%02X ", ipinfo->tos);
385         }
386         if (ipinfo->bitmask & EBT_IP_PROTO) {
387                 struct protoent *pe;
388
389                 printf("--ip-proto ");
390                 if (ipinfo->invflags & EBT_IP_PROTO)
391                         printf("! ");
392                 pe = getprotobynumber(ipinfo->protocol);
393                 if (pe == NULL) {
394                         printf("%d ", ipinfo->protocol);
395                 } else {
396                         printf("%s ", pe->p_name);
397                 }
398         }
399         if (ipinfo->bitmask & EBT_IP_SPORT) {
400                 printf("--ip-sport ");
401                 if (ipinfo->invflags & EBT_IP_SPORT) {
402                         printf("! ");
403                 }
404                 print_port_range(ipinfo->sport);
405         }
406         if (ipinfo->bitmask & EBT_IP_DPORT) {
407                 printf("--ip-dport ");
408                 if (ipinfo->invflags & EBT_IP_DPORT) {
409                         printf("! ");
410                 }
411                 print_port_range(ipinfo->dport);
412         }
413 }
414
415 static int compare(const struct ebt_entry_match *m1,
416    const struct ebt_entry_match *m2)
417 {
418         struct ebt_ip_info *ipinfo1 = (struct ebt_ip_info *)m1->data;
419         struct ebt_ip_info *ipinfo2 = (struct ebt_ip_info *)m2->data;
420
421         if (ipinfo1->bitmask != ipinfo2->bitmask)
422                 return 0;
423         if (ipinfo1->invflags != ipinfo2->invflags)
424                 return 0;
425         if (ipinfo1->bitmask & EBT_IP_SOURCE) {
426                 if (ipinfo1->saddr != ipinfo2->saddr)
427                         return 0;
428                 if (ipinfo1->smsk != ipinfo2->smsk)
429                         return 0;
430         }
431         if (ipinfo1->bitmask & EBT_IP_DEST) {
432                 if (ipinfo1->daddr != ipinfo2->daddr)
433                         return 0;
434                 if (ipinfo1->dmsk != ipinfo2->dmsk)
435                         return 0;
436         }
437         if (ipinfo1->bitmask & EBT_IP_TOS) {
438                 if (ipinfo1->tos != ipinfo2->tos)
439                         return 0;
440         }
441         if (ipinfo1->bitmask & EBT_IP_PROTO) {
442                 if (ipinfo1->protocol != ipinfo2->protocol)
443                         return 0;
444         }
445         if (ipinfo1->bitmask & EBT_IP_SPORT) {
446                 if (ipinfo1->sport[0] != ipinfo2->sport[0] ||
447                    ipinfo1->sport[1] != ipinfo2->sport[1])
448                         return 0;
449         }
450         if (ipinfo1->bitmask & EBT_IP_DPORT) {
451                 if (ipinfo1->dport[0] != ipinfo2->dport[0] ||
452                    ipinfo1->dport[1] != ipinfo2->dport[1])
453                         return 0;
454         }
455         return 1;
456 }
457
458 static struct ebt_u_match ip_match =
459 {
460         .name           = EBT_IP_MATCH,
461         .size           = sizeof(struct ebt_ip_info),
462         .help           = print_help,
463         .init           = init,
464         .parse          = parse,
465         .final_check    = final_check,
466         .print          = print,
467         .compare        = compare,
468         .extra_ops      = opts,
469 };
470
471 static void _init(void) __attribute((constructor));
472 static void _init(void)
473 {
474         register_match(&ip_match);
475 }