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>
22 #include <sys/types.h>
26 #include <linux/netfilter_ipv4/ipt_geoip.h>
28 static void help(void)
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"
36 " [!] --dst-cc, --destination-country country[,country,country,...]\n"
37 " Match packet going to (one of)\n"
38 " the specified country(ies)\n"
40 " NOTE: The country is inputed by its ISO3166 code.\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' },
55 init(struct ipt_entry_match
*m
, unsigned int *nfcache
)
59 /* NOT IMPLEMENTED YET
60 static void geoip_free(struct geoip_info *oldmem)
68 } __attribute__ ((packed
));
71 get_country_subnets(u_int16_t cc
, u_int32_t
*count
)
74 struct geoip_subnet
*subnets
;
75 struct geoip_index
*index
;
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
)
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
));
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
);
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
));
125 exit_error(OTHER_PROBLEM
,
126 "geoip match: insufficient memory available");
128 fread(subnets
, db_nsubnets
* sizeof(struct geoip_subnet
), 1, dbfd
);
132 *count
= db_nsubnets
;
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
));
145 ginfo
->subnets
= get_country_subnets(cc
, &ginfo
->count
);
152 check_geoip_cc(char *cc
, u_int16_t cc_used
[], u_int8_t count
)
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
]);
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.
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!
187 /* Based on libipt_multiport.c parsing code. */
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;
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");
212 if (cp
) exit_error(PARAMETER_PROBLEM
,
213 "geoip match: too many countries specified");
216 if (count
== 0) exit_error(PARAMETER_PROBLEM
,
217 "geoip match: don't know what happened");
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
;
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
;
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
;
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;
268 final_check(unsigned int flags
)
271 exit_error(PARAMETER_PROBLEM
,
272 "geoip match: missing arguments");
276 print(const struct ipt_ip
*ip
,
277 const struct ipt_entry_match
*match
,
280 const struct ipt_geoip_info
*info
281 = (const struct ipt_geoip_info
*)match
->data
;
285 if (info
->flags
& IPT_GEOIP_SRC
)
287 else printf("Destination ");
290 printf("countries: ");
291 else printf("country: ");
293 if (info
->flags
& IPT_GEOIP_INV
)
296 for (i
= 0; i
< info
->count
; i
++)
297 printf("%s%c%c", i
? "," : "", COUNTRY(info
->cc
[i
]));
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
;
309 if (info
->flags
& IPT_GEOIP_INV
)
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
]));
321 static struct iptables_match geoip
= {
323 .version
= IPTABLES_VERSION
,
324 .size
= IPT_ALIGN(sizeof(struct ipt_geoip_info
)),
325 .userspacesize
= offsetof(struct ipt_geoip_info
, mem
),
329 .final_check
= &final_check
,
337 register_match(&geoip
);