K2.6 patches and update.
[tomato.git] / release / src / router / iptables / extensions / libipt_geoip.c
blobb3ad849b50f4de41f2d211f3c9ad1d5f1349e6b1
1 /* Shared library add-on to iptables to add geoip match support.
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * Copyright (c) 2004 Cookinglinux
10 * For comments, bugs or suggestions, please contact
11 * Samuel Jean <sjean at cookinglinux.org>
12 * Nicolas Bouliane <nib at cookinglinux.org>
15 #include <stdio.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #include <getopt.h>
19 #include <ctype.h>
20 #include <stddef.h>
21 #include <errno.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
25 #include <iptables.h>
26 #include <linux/netfilter_ipv4/ipt_geoip.h>
28 static void help(void)
30 printf (
31 "GeoIP v%s options:\n"
32 " [!] --src-cc, --source-country country[,country,country,...]\n"
33 " Match packet coming from (one of)\n"
34 " the specified country(ies)\n"
35 "\n"
36 " [!] --dst-cc, --destination-country country[,country,country,...]\n"
37 " Match packet going to (one of)\n"
38 " the specified country(ies)\n"
39 "\n"
40 " NOTE: The country is inputed by its ISO3166 code.\n"
41 "\n"
42 "\n", IPTABLES_VERSION
46 static struct option opts[] = {
47 { "dst-cc", 1, 0, '2' }, /* Alias for --destination-country */
48 { "destination-country", 1, 0, '2' },
49 { "src-cc", 1, 0, '1' }, /* Alias for --source-country */
50 { "source-country", 1, 0, '1' },
51 { 0 }
54 static void
55 init(struct ipt_entry_match *m, unsigned int *nfcache)
59 /* NOT IMPLEMENTED YET
60 static void geoip_free(struct geoip_info *oldmem)
65 struct geoip_index {
66 u_int16_t cc;
67 u_int32_t offset;
68 } __attribute__ ((packed));
70 struct geoip_subnet *
71 get_country_subnets(u_int16_t cc, u_int32_t *count)
73 FILE *ixfd, *dbfd;
74 struct geoip_subnet *subnets;
75 struct geoip_index *index;
76 struct stat buf;
78 size_t idxsz;
79 u_int16_t i;
81 u_int16_t db_cc = 0;
82 u_int16_t db_nsubnets = 0;
84 if ((ixfd = fopen("/var/geoip/geoipdb.idx", "r")) == NULL) {
85 perror("/var/geoip/geoipdb.idx");
86 exit_error(OTHER_PROBLEM,
87 "geoip match: cannot open geoip's database index file");
90 stat("/var/geoip/geoipdb.idx", &buf);
91 idxsz = buf.st_size/sizeof(struct geoip_index);
92 index = (struct geoip_index *)malloc(buf.st_size);
94 fread(index, buf.st_size, 1, ixfd);
96 for (i = 0; i < idxsz; i++)
97 if (cc == index[i].cc)
98 break;
100 if (cc != index[i].cc)
101 exit_error(OTHER_PROBLEM,
102 "geoip match: sorry, '%c%c' isn't in the database\n", COUNTRY(cc));
104 fclose(ixfd);
106 if ((dbfd = fopen("/var/geoip/geoipdb.bin", "r")) == NULL) {
107 perror("/var/geoip/geoipdb.bin");
108 exit_error(OTHER_PROBLEM,
109 "geoip match: cannot open geoip's database file");
112 fseek(dbfd, index[i].offset, SEEK_SET);
113 fread(&db_cc, sizeof(u_int16_t), 1, dbfd);
115 if (db_cc != cc)
116 exit_error(OTHER_PROBLEM,
117 "geoip match: this shouldn't happened, the database might be corrupted, or there's a bug.\n"
118 "you should contact maintainers");
120 fread(&db_nsubnets, sizeof(u_int16_t), 1, dbfd);
122 subnets = (struct geoip_subnet*)malloc(db_nsubnets * sizeof(struct geoip_subnet));
124 if (!subnets)
125 exit_error(OTHER_PROBLEM,
126 "geoip match: insufficient memory available");
128 fread(subnets, db_nsubnets * sizeof(struct geoip_subnet), 1, dbfd);
130 fclose(dbfd);
131 free(index);
132 *count = db_nsubnets;
133 return subnets;
136 static struct geoip_info *
137 load_geoip_cc(u_int16_t cc)
139 static struct geoip_info *ginfo;
140 ginfo = malloc(sizeof(struct geoip_info));
142 if (!ginfo)
143 return NULL;
145 ginfo->subnets = get_country_subnets(cc, &ginfo->count);
146 ginfo->cc = cc;
148 return ginfo;
151 static u_int16_t
152 check_geoip_cc(char *cc, u_int16_t cc_used[], u_int8_t count)
154 u_int8_t i;
155 u_int16_t cc_int16;
157 if (strlen(cc) != 2) /* Country must be 2 chars long according
158 to the ISO3166 standard */
159 exit_error(PARAMETER_PROBLEM,
160 "geoip match: invalid country code '%s'", cc);
162 // Verification will fail if chars aren't uppercased.
163 // Make sure they are..
164 for (i = 0; i < 2; i++)
165 if (isalnum(cc[i]) != 0)
166 cc[i] = toupper(cc[i]);
167 else
168 exit_error(PARAMETER_PROBLEM,
169 "geoip match: invalid country code '%s'", cc);
171 /* Convert chars into a single 16 bit integer.
172 * FIXME: This assumes that a country code is
173 * exactly 2 chars long. If this is
174 * going to change someday, this whole
175 * match will need to be rewritten, anyway.
176 * - SJ */
177 cc_int16 = (cc[0]<<8) + cc[1];
179 // Check for presence of value in cc_used
180 for (i = 0; i < count; i++)
181 if (cc_int16 == cc_used[i])
182 return 0; // Present, skip it!
184 return cc_int16;
187 /* Based on libipt_multiport.c parsing code. */
188 static u_int8_t
189 parse_geoip_cc(const char *ccstr, u_int16_t *cc, struct geoip_info **mem)
191 char *buffer, *cp, *next;
192 u_int8_t i, count = 0;
193 u_int16_t cctmp;
195 buffer = strdup(ccstr);
196 if (!buffer) exit_error(OTHER_PROBLEM,
197 "geoip match: insufficient memory available");
199 for (cp = buffer, i = 0; cp && i < IPT_GEOIP_MAX; cp = next, i++)
201 next = strchr(cp, ',');
202 if (next) *next++ = '\0';
204 if ((cctmp = check_geoip_cc(cp, cc, count)) != 0) {
205 if ((mem[count++] = load_geoip_cc(cctmp)) == NULL)
206 exit_error(OTHER_PROBLEM,
207 "geoip match: insufficient memory available");
208 cc[count-1] = cctmp;
212 if (cp) exit_error(PARAMETER_PROBLEM,
213 "geoip match: too many countries specified");
214 free(buffer);
216 if (count == 0) exit_error(PARAMETER_PROBLEM,
217 "geoip match: don't know what happened");
219 return count;
222 static int
223 parse(int c, char **argv, int invert, unsigned int *flags,
224 const struct ipt_entry *entry,
225 unsigned int *nfcache,
226 struct ipt_entry_match **match)
228 struct ipt_geoip_info *info
229 = (struct ipt_geoip_info *)(*match)->data;
231 switch(c) {
232 case '1':
233 // Ensure that IPT_GEOIP_SRC *OR* IPT_GEOIP_DST haven't been used yet.
234 if (*flags & (IPT_GEOIP_SRC | IPT_GEOIP_DST))
235 exit_error(PARAMETER_PROBLEM,
236 "geoip match: only use --source-country *OR* --destination-country once!");
238 *flags |= IPT_GEOIP_SRC;
239 *nfcache |= NFC_IP_SRC;
240 break;
242 case '2':
243 // Ensure that IPT_GEOIP_SRC *OR* IPT_GEOIP_DST haven't been used yet.
244 if (*flags & (IPT_GEOIP_SRC | IPT_GEOIP_DST))
245 exit_error(PARAMETER_PROBLEM,
246 "geoip match: only use --source-country *OR* --destination-country once!");
248 *flags |= IPT_GEOIP_DST;
249 *nfcache |= NFC_IP_DST;
250 break;
252 default:
253 return 0;
256 if (invert)
257 *flags |= IPT_GEOIP_INV;
259 info->count = parse_geoip_cc(argv[optind-1], info->cc, info->mem);
260 info->flags = *flags;
261 info->refcount = NULL;
262 //info->fini = &geoip_free;
264 return 1;
267 static void
268 final_check(unsigned int flags)
270 if (!flags)
271 exit_error(PARAMETER_PROBLEM,
272 "geoip match: missing arguments");
275 static void
276 print(const struct ipt_ip *ip,
277 const struct ipt_entry_match *match,
278 int numeric)
280 const struct ipt_geoip_info *info
281 = (const struct ipt_geoip_info *)match->data;
283 u_int8_t i;
285 if (info->flags & IPT_GEOIP_SRC)
286 printf("Source ");
287 else printf("Destination ");
289 if (info->count > 1)
290 printf("countries: ");
291 else printf("country: ");
293 if (info->flags & IPT_GEOIP_INV)
294 printf("! ");
296 for (i = 0; i < info->count; i++)
297 printf("%s%c%c", i ? "," : "", COUNTRY(info->cc[i]));
298 printf(" ");
301 static void
302 save(const struct ipt_ip *ip,
303 const struct ipt_entry_match *match)
305 const struct ipt_geoip_info *info
306 = (const struct ipt_geoip_info *)match->data;
307 u_int8_t i;
309 if (info->flags & IPT_GEOIP_INV)
310 printf("! ");
312 if (info->flags & IPT_GEOIP_SRC)
313 printf("--source-country ");
314 else printf("--destination-country ");
316 for (i = 0; i < info->count; i++)
317 printf("%s%c%c", i ? "," : "", COUNTRY(info->cc[i]));
318 printf(" ");
321 static struct iptables_match geoip = {
322 .name = "geoip",
323 .version = IPTABLES_VERSION,
324 .size = IPT_ALIGN(sizeof(struct ipt_geoip_info)),
325 .userspacesize = offsetof(struct ipt_geoip_info, mem),
326 .help = &help,
327 .init = &init,
328 .parse = &parse,
329 .final_check = &final_check,
330 .print = &print,
331 .save = &save,
332 .extra_opts = opts
335 void _init(void)
337 register_match(&geoip);