changing trunk/trunk to trunk
[iptables.git] / extensions / libip6t_dst.c
1 /* Shared library add-on to ip6tables to add Hop-by-Hop and Dst headers support. */
2 #include <stdio.h>
3 #include <netdb.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <getopt.h>
7 #include <errno.h>
8 #include <ip6tables.h>
9 #include <linux/netfilter_ipv6/ip6t_opts.h>
10 #include <sys/types.h>
11 #include <sys/socket.h>
12 #include <arpa/inet.h>
13
14 /* Function which prints out usage message. */
15 static void dst_help(void)
16 {
17         printf(
18 "dst match options:\n"
19 "  --dst-len [!] length          total length of this header\n"
20 "  --dst-opts TYPE[:LEN][,TYPE[:LEN]...]\n"
21 "                                Options and its length (list, max: %d)\n",
22 IP6T_OPTS_OPTSNR);
23 }
24
25 static const struct option dst_opts[] = {
26         { .name = "dst-len",        .has_arg = 1, .val = '1' },
27         { .name = "dst-opts",       .has_arg = 1, .val = '2' },
28         { .name = "dst-not-strict", .has_arg = 1, .val = '3' },
29         { .name = NULL }
30 };
31
32 static u_int32_t
33 parse_opts_num(const char *idstr, const char *typestr)
34 {
35         unsigned long int id;
36         char* ep;
37
38         id = strtoul(idstr, &ep, 0);
39
40         if ( idstr == ep ) {
41                 exit_error(PARAMETER_PROBLEM,
42                            "dst: no valid digits in %s `%s'", typestr, idstr);
43         }
44         if ( id == ULONG_MAX  && errno == ERANGE ) {
45                 exit_error(PARAMETER_PROBLEM,
46                            "%s `%s' specified too big: would overflow",
47                            typestr, idstr);
48         }
49         if ( *idstr != '\0'  && *ep != '\0' ) {
50                 exit_error(PARAMETER_PROBLEM,
51                            "dst: error parsing %s `%s'", typestr, idstr);
52         }
53         return (u_int32_t) id;
54 }
55
56 static int
57 parse_options(const char *optsstr, u_int16_t *opts)
58 {
59         char *buffer, *cp, *next, *range;
60         unsigned int i;
61         
62         buffer = strdup(optsstr);
63         if (!buffer)
64                 exit_error(OTHER_PROBLEM, "strdup failed");
65                         
66         for (cp = buffer, i = 0; cp && i < IP6T_OPTS_OPTSNR; cp = next, i++)
67         {
68                 next = strchr(cp, ',');
69
70                 if (next)
71                         *next++='\0';
72
73                 range = strchr(cp, ':');
74
75                 if (range) {
76                         if (i == IP6T_OPTS_OPTSNR-1)
77                                 exit_error(PARAMETER_PROBLEM,
78                                            "too many ports specified");
79                         *range++ = '\0';
80                 }
81
82                 opts[i] = (u_int16_t)((parse_opts_num(cp,"opt") & 0x000000FF)<<8); 
83                 if (range) {
84                         if (opts[i] == 0)
85                                 exit_error(PARAMETER_PROBLEM,
86                                         "PAD0 hasn't got length");
87                         opts[i] |= (u_int16_t)(parse_opts_num(range,"length") &
88                                         0x000000FF);
89                 } else
90                         opts[i] |= (0x00FF);
91
92 #ifdef DEBUG
93                 printf("opts str: %s %s\n", cp, range);
94                 printf("opts opt: %04X\n", opts[i]);
95 #endif
96         }
97
98         if (cp)
99                 exit_error(PARAMETER_PROBLEM, "too many addresses specified");
100
101         free(buffer);
102
103 #ifdef DEBUG
104         printf("addr nr: %d\n", i);
105 #endif
106
107         return i;
108 }
109
110 /* Initialize the match. */
111 static void dst_init(struct xt_entry_match *m)
112 {
113         struct ip6t_opts *optinfo = (struct ip6t_opts *)m->data;
114
115         optinfo->hdrlen = 0;
116         optinfo->flags = 0;
117         optinfo->invflags = 0;
118         optinfo->optsnr = 0;
119 }
120
121 /* Function which parses command options; returns true if it
122    ate an option */
123 static int dst_parse(int c, char **argv, int invert, unsigned int *flags,
124                      const void *entry, struct xt_entry_match **match)
125 {
126         struct ip6t_opts *optinfo = (struct ip6t_opts *)(*match)->data;
127
128         switch (c) {
129         case '1':
130                 if (*flags & IP6T_OPTS_LEN)
131                         exit_error(PARAMETER_PROBLEM,
132                                    "Only one `--dst-len' allowed");
133                 check_inverse(optarg, &invert, &optind, 0);
134                 optinfo->hdrlen = parse_opts_num(argv[optind-1], "length");
135                 if (invert)
136                         optinfo->invflags |= IP6T_OPTS_INV_LEN;
137                 optinfo->flags |= IP6T_OPTS_LEN;
138                 *flags |= IP6T_OPTS_LEN;
139                 break;
140         case '2':
141                 if (*flags & IP6T_OPTS_OPTS)
142                         exit_error(PARAMETER_PROBLEM,
143                                    "Only one `--dst-opts' allowed");
144                 check_inverse(optarg, &invert, &optind, 0);
145                 if (invert)
146                         exit_error(PARAMETER_PROBLEM,
147                                 " '!' not allowed with `--dst-opts'");
148                 optinfo->optsnr = parse_options(argv[optind-1], optinfo->opts);
149                 optinfo->flags |= IP6T_OPTS_OPTS;
150                 *flags |= IP6T_OPTS_OPTS;
151                 break;
152         case '3':
153                 if (*flags & IP6T_OPTS_NSTRICT)
154                         exit_error(PARAMETER_PROBLEM,
155                                    "Only one `--dst-not-strict' allowed");
156                 if ( !(*flags & IP6T_OPTS_OPTS) )
157                         exit_error(PARAMETER_PROBLEM,
158                                    "`--dst-opts ...' required before "
159                                    "`--dst-not-strict'");
160                 optinfo->flags |= IP6T_OPTS_NSTRICT;
161                 *flags |= IP6T_OPTS_NSTRICT;
162                 break;
163         default:
164                 return 0;
165         }
166
167         return 1;
168 }
169
170 static void
171 print_options(unsigned int optsnr, u_int16_t *optsp)
172 {
173         unsigned int i;
174
175         for(i = 0; i < optsnr; i++) {
176                 printf("%d", (optsp[i] & 0xFF00) >> 8);
177
178                 if ((optsp[i] & 0x00FF) != 0x00FF)
179                         printf(":%d", (optsp[i] & 0x00FF));
180
181                 printf("%c", (i != optsnr - 1) ? ',' : ' ');
182         }
183 }
184
185 /* Prints out the union ip6t_matchinfo. */
186 static void dst_print(const void *ip, const struct xt_entry_match *match,
187                       int numeric)
188 {
189         const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data;
190
191         printf("dst ");
192         if (optinfo->flags & IP6T_OPTS_LEN)
193                 printf("length:%s%u ",
194                         optinfo->invflags & IP6T_OPTS_INV_LEN ? "!" : "",
195                         optinfo->hdrlen);
196
197         if (optinfo->flags & IP6T_OPTS_OPTS)
198                 printf("opts ");
199
200         print_options(optinfo->optsnr, (u_int16_t *)optinfo->opts);
201
202         if (optinfo->flags & IP6T_OPTS_NSTRICT)
203                 printf("not-strict ");
204
205         if (optinfo->invflags & ~IP6T_OPTS_INV_MASK)
206                 printf("Unknown invflags: 0x%X ",
207                        optinfo->invflags & ~IP6T_OPTS_INV_MASK);
208 }
209
210 /* Saves the union ip6t_matchinfo in parsable form to stdout. */
211 static void dst_save(const void *ip, const struct xt_entry_match *match)
212 {
213         const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data;
214
215         if (optinfo->flags & IP6T_OPTS_LEN) {
216                 printf("--dst-len %s%u ",
217                         (optinfo->invflags & IP6T_OPTS_INV_LEN) ? "! " : "", 
218                         optinfo->hdrlen);
219         }
220
221         if (optinfo->flags & IP6T_OPTS_OPTS)
222                 printf("--dst-opts ");
223
224         print_options(optinfo->optsnr, (u_int16_t *)optinfo->opts);
225
226         if (optinfo->flags & IP6T_OPTS_NSTRICT)
227                 printf("--dst-not-strict ");
228 }
229
230 static struct xtables_match dst_mt6_reg = {
231         .name          = "dst",
232         .version       = XTABLES_VERSION,
233         .family        = PF_INET6,
234         .size          = XT_ALIGN(sizeof(struct ip6t_opts)),
235         .userspacesize = XT_ALIGN(sizeof(struct ip6t_opts)),
236         .help          = dst_help,
237         .init          = dst_init,
238         .parse         = dst_parse,
239         .print         = dst_print,
240         .save          = dst_save,
241         .extra_opts    = dst_opts,
242 };
243
244 void
245 _init(void)
246 {
247         xtables_register_match(&dst_mt6_reg);
248 }