iptables-1.3.2-20050720
[iptables.git] / extensions / libip6t_multiport.c
1 /* Shared library add-on to iptables to add multiple TCP port support. */
2 #include <stdio.h>
3 #include <netdb.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <getopt.h>
7 #include <ip6tables.h>
8 #include <linux/netfilter_ipv6/ip6t_multiport.h>
9
10 /* Function which prints out usage message. */
11 static void
12 help(void)
13 {
14         printf(
15 "multiport v%s options:\n"
16 " --source-ports port[,port,port...]\n"
17 " --sports ...\n"
18 "                               match source port(s)\n"
19 " --destination-ports port[,port,port...]\n"
20 " --dports ...\n"
21 "                               match destination port(s)\n"
22 " --ports port[,port,port]\n"
23 "                               match both source and destination port(s)\n",
24 IPTABLES_VERSION);
25 }
26
27 static struct option opts[] = {
28         { "source-ports", 1, 0, '1' },
29         { "sports", 1, 0, '1' }, /* synonym */
30         { "destination-ports", 1, 0, '2' },
31         { "dports", 1, 0, '2' }, /* synonym */
32         { "ports", 1, 0, '3' },
33         {0}
34 };
35
36 static int
37 service_to_port(const char *name, const char *proto)
38 {
39         struct servent *service;
40
41         if ((service = getservbyname(name, proto)) != NULL)
42                 return ntohs((unsigned short) service->s_port);
43
44                 return -1;
45 }
46
47 static u_int16_t
48 parse_port(const char *port, const char *proto)
49 {
50         unsigned int portnum;
51
52         if ((string_to_number(port, 0, 65535, &portnum)) != -1 ||
53             (portnum = service_to_port(port, proto)) != -1)
54                 return (u_int16_t)portnum;
55
56         exit_error(PARAMETER_PROBLEM,
57                    "invalid port/service `%s' specified", port);
58 }
59
60 static unsigned int
61 parse_multi_ports(const char *portstring, u_int16_t *ports, const char *proto)
62 {
63         char *buffer, *cp, *next;
64         unsigned int i;
65
66         buffer = strdup(portstring);
67         if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
68
69         for (cp=buffer, i=0; cp && i<IP6T_MULTI_PORTS; cp=next,i++)
70         {
71                 next=strchr(cp, ',');
72                 if (next) *next++='\0';
73                 ports[i] = parse_port(cp, proto);
74         }
75         if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified");
76         free(buffer);
77         return i;
78 }
79
80 /* Initialize the match. */
81 static void
82 init(struct ip6t_entry_match *m, unsigned int *nfcache)
83 {
84 }
85
86 static const char *
87 check_proto(const struct ip6t_entry *entry)
88 {
89         if (entry->ipv6.proto == IPPROTO_TCP)
90                 return "tcp";
91         else if (entry->ipv6.proto == IPPROTO_UDP)
92                 return "udp";
93         else if (!entry->ipv6.proto)
94                 exit_error(PARAMETER_PROBLEM,
95                            "multiport needs `-p tcp' or `-p udp'");
96         else
97                 exit_error(PARAMETER_PROBLEM,
98                            "multiport only works with TCP or UDP");
99 }
100
101 /* Function which parses command options; returns true if it
102    ate an option */
103 static int
104 parse(int c, char **argv, int invert, unsigned int *flags,
105       const struct ip6t_entry *entry,
106       unsigned int *nfcache,
107       struct ip6t_entry_match **match)
108 {
109         const char *proto;
110         struct ip6t_multiport *multiinfo
111                 = (struct ip6t_multiport *)(*match)->data;
112
113         switch (c) {
114         case '1':
115                 check_inverse(argv[optind-1], &invert, &optind, 0);
116                 proto = check_proto(entry);
117                 multiinfo->count = parse_multi_ports(argv[optind-1],
118                                                      multiinfo->ports, proto);
119                 multiinfo->flags = IP6T_MULTIPORT_SOURCE;
120                 break;
121
122         case '2':
123                 check_inverse(argv[optind-1], &invert, &optind, 0);
124                 proto = check_proto(entry);
125                 multiinfo->count = parse_multi_ports(argv[optind-1],
126                                                      multiinfo->ports, proto);
127                 multiinfo->flags = IP6T_MULTIPORT_DESTINATION;
128                 break;
129
130         case '3':
131                 check_inverse(argv[optind-1], &invert, &optind, 0);
132                 proto = check_proto(entry);
133                 multiinfo->count = parse_multi_ports(argv[optind-1],
134                                                      multiinfo->ports, proto);
135                 multiinfo->flags = IP6T_MULTIPORT_EITHER;
136                 break;
137
138         default:
139                 return 0;
140         }
141
142         if (invert)
143                 exit_error(PARAMETER_PROBLEM,
144                            "multiport does not support invert");
145
146         if (*flags)
147                 exit_error(PARAMETER_PROBLEM,
148                            "multiport can only have one option");
149         *flags = 1;
150         return 1;
151 }
152
153 /* Final check; must specify something. */
154 static void
155 final_check(unsigned int flags)
156 {
157         if (!flags)
158                 exit_error(PARAMETER_PROBLEM, "multiport expection an option");
159 }
160
161 static char *
162 port_to_service(int port, u_int8_t proto)
163 {
164         struct servent *service;
165
166         if ((service = getservbyport(htons(port),
167                                      proto == IPPROTO_TCP ? "tcp" : "udp")))
168                 return service->s_name;
169
170         return NULL;
171 }
172
173 static void
174 print_port(u_int16_t port, u_int8_t protocol, int numeric)
175 {
176         char *service;
177
178         if (numeric || (service = port_to_service(port, protocol)) == NULL)
179                 printf("%u", port);
180         else
181                 printf("%s", service);
182 }
183
184 /* Prints out the matchinfo. */
185 static void
186 print(const struct ip6t_ip6 *ip,
187       const struct ip6t_entry_match *match,
188       int numeric)
189 {
190         const struct ip6t_multiport *multiinfo
191                 = (const struct ip6t_multiport *)match->data;
192         unsigned int i;
193
194         printf("multiport ");
195
196         switch (multiinfo->flags) {
197         case IP6T_MULTIPORT_SOURCE:
198                 printf("sports ");
199                 break;
200
201         case IP6T_MULTIPORT_DESTINATION:
202                 printf("dports ");
203                 break;
204
205         case IP6T_MULTIPORT_EITHER:
206                 printf("ports ");
207                 break;
208
209         default:
210                 printf("ERROR ");
211                 break;
212         }
213
214         for (i=0; i < multiinfo->count; i++) {
215                 printf("%s", i ? "," : "");
216                 print_port(multiinfo->ports[i], ip->proto, numeric);
217         }
218         printf(" ");
219 }
220
221 /* Saves the union ip6t_matchinfo in parsable form to stdout. */
222 static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
223 {
224         const struct ip6t_multiport *multiinfo
225                 = (const struct ip6t_multiport *)match->data;
226         unsigned int i;
227
228         switch (multiinfo->flags) {
229         case IP6T_MULTIPORT_SOURCE:
230                 printf("--sports ");
231                 break;
232
233         case IP6T_MULTIPORT_DESTINATION:
234                 printf("--dports ");
235                 break;
236
237         case IP6T_MULTIPORT_EITHER:
238                 printf("--ports ");
239                 break;
240         }
241
242         for (i=0; i < multiinfo->count; i++) {
243                 printf("%s", i ? "," : "");
244                 print_port(multiinfo->ports[i], ip->proto, 1);
245         }
246         printf(" ");
247 }
248
249 static struct ip6tables_match multiport = {
250         .name           = "multiport",
251         .version        = IPTABLES_VERSION,
252         .size           = IP6T_ALIGN(sizeof(struct ip6t_multiport)),
253         .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_multiport)),
254         .help           = &help,
255         .init           = &init,
256         .parse          = &parse,
257         .final_check    = &final_check,
258         .print          = &print,
259         .save           = &save,
260         .extra_opts     = opts,
261 };
262
263 void
264 _init(void)
265 {
266         register_match6(&multiport);
267 }