3
grepcidr 1.3 - Filter IP addresses matching IPv4 CIDR specification
4
Copyright (C) 2004, 2005 Jem E. Berkes <jberkes@pc-tools.net>
7
This program is free software; you can redistribute it and/or modify
8
it under the terms of the GNU General Public License as published by
9
the Free Software Foundation; either version 2 of the License, or
10
(at your option) any later version.
12
This program is distributed in the hope that it will be useful,
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
GNU General Public License for more details.
17
You should have received a copy of the GNU General Public License
18
along with this program; if not, write to the Free Software
19
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31
#define EXIT_NOMATCH 1
34
#define TXT_VERSION "grepcidr 1.3\nCopyright (C) 2004, 2005 Jem E. Berkes <jberkes@pc-tools.net>\n"
35
#define TXT_USAGE "Usage:\n" \
36
"\tgrepcidr [-V] [-c] [-v] PATTERN [FILE]\n" \
37
"\tgrepcidr [-V] [-c] [-v] [-e PATTERN | -f FILE] [FILE]\n"
39
#define TOKEN_SEPS "\t,\r\n" /* so user can specify multiple patterns on command line */
40
#define INIT_NETWORKS 8192
43
Specifies a network. Whether originally in CIDR format (IP/mask)
44
or a range of IPs (IP_start-IP_end), spec is converted to a range.
45
The range is min to max (32-bit IPs) inclusive.
53
/* Macro to test for valid IP address in four integers */
54
#define VALID_IP(IP) ((IP[0]<256) && (IP[1]<256) && (IP[2]<256) && (IP[3]<256))
55
/* Macro to build 32-bit IP from four integers */
56
#define BUILD_IP(IP) ((IP[0]<<24) | (IP[1]<<16) | (IP[2]<<8) | IP[3])
59
/* Global variables */
60
unsigned int patterns = 0; /* total patterns in array */
61
unsigned int capacity = 0; /* current capacity of array */
62
struct netspec* array = NULL; /* array of patterns, network specs */
65
Insert new spec inside array of network spec
66
Dynamically grow array buffer as needed
67
The array must have already been initially allocated, with valid capacity
69
void array_insert(struct netspec* newspec)
71
if (patterns == capacity)
74
array = realloc(array, capacity*sizeof(struct netspec));
76
array[patterns++] = *newspec;
81
Convert IP address string to 32-bit integer version
84
unsigned int ip_to_uint(const char* ip)
86
unsigned int IP[4]; /* 4 octets for IP address */
87
if ((sscanf(ip, "%u.%u.%u.%u", &IP[0], &IP[1], &IP[2], &IP[3]) == 4) && VALID_IP(IP))
95
Given string, fills in the struct netspec (must be allocated)
96
Accept CIDR IP/mask format or IP_start-IP_end range.
97
Returns true (nonzero) on success, false (zero) on failure.
99
int net_parse(const char* line, struct netspec* spec)
101
unsigned int IP1[4], IP2[4];
102
int maskbits = 32; /* if using CIDR IP/mask format */
104
/* Try parsing IP/mask, CIDR format */
105
if (strchr(line, '/') && (sscanf(line, "%u.%u.%u.%u/%d", &IP1[0], &IP1[1], &IP1[2], &IP1[3], &maskbits) == 5)
106
&& VALID_IP(IP1) && (maskbits >= 1) && (maskbits <= 32))
108
spec->min = BUILD_IP(IP1) & (~((1 << (32-maskbits))-1) & 0xFFFFFFFF);
109
spec->max = spec->min | (((1 << (32-maskbits))-1) & 0xFFFFFFFF);
112
/* Try parsing a range. Spaces around hyphen are optional. */
113
else if (strchr(line, '-') && (sscanf(line, "%u.%u.%u.%u - %u.%u.%u.%u", &IP1[0], &IP1[1], &IP1[2], &IP1[3],
114
&IP2[0], &IP2[1], &IP2[2], &IP2[3]) == 8) && VALID_IP(IP1) && VALID_IP(IP2))
116
spec->min = BUILD_IP(IP1);
117
spec->max = BUILD_IP(IP2);
118
if (spec->max >= spec->min)
123
/* Try simple IP address */
124
else if ((sscanf(line, "%u.%u.%u.%u", &IP1[0], &IP1[1], &IP1[2], &IP1[3]) == 4) && VALID_IP(IP1))
126
spec->min = BUILD_IP(IP1);
127
spec->max = spec->min;
130
return 0; /* could not parse */
134
/* Compare two netspecs, for sorting. Comparison is done on minimum of range */
135
int netsort(const void* a, const void* b)
137
unsigned int c1 = ((struct netspec*)a)->min;
138
unsigned int c2 = ((struct netspec*)b)->min;
139
if (c1 < c2) return -1;
140
if (c1 > c2) return +1;
144
/* Compare two netspecs, for searching. Test if key (only min) is inside range */
145
int netsearch(const void* a, const void* b)
147
unsigned int key = ((struct netspec*)a)->min;
148
unsigned int min = ((struct netspec*)b)->min;
149
unsigned int max = ((struct netspec*)b)->max;
150
if (key < min) return -1;
151
if (key > max) return +1;
156
int main(int argc, char* argv[])
158
static char shortopts[] = "ce:f:vV";
159
FILE* inp_stream = stdin; /* input stream, list of IPs to match */
160
char* pat_filename = NULL; /* filename containing patterns */
161
char* pat_strings = NULL; /* pattern strings on command line */
162
unsigned int counting = 0; /* when non-zero, counts matches */
163
int invert = 0; /* flag for inverted mode */
166
int anymatch = 0; /* did anything match? for exit code */
170
fprintf(stderr, TXT_USAGE);
174
while ((foundopt = getopt(argc, argv, shortopts)) != -1)
191
pat_strings = optarg;
195
pat_filename = optarg;
199
fprintf(stderr, TXT_USAGE);
204
if (!pat_filename && !pat_strings)
207
pat_strings = argv[optind++];
210
fprintf(stderr, "Specify PATTERN or -f FILE to read patterns from\n");
217
inp_stream = fopen(argv[optind], "r");
220
perror(argv[optind]);
225
/* Initial array allocation */
226
capacity = INIT_NETWORKS;
227
array = (struct netspec*) malloc(capacity*sizeof(struct netspec));
229
/* Load patterns defining networks */
232
FILE* data = fopen(pat_filename, "r");
235
while (fgets(line, sizeof(line), data))
238
if ((*line != '#') && net_parse(line, &spec))
245
perror(pat_filename);
251
char* token = strtok(pat_strings, TOKEN_SEPS);
255
if (net_parse(token, &spec))
257
token = strtok(NULL, TOKEN_SEPS);
261
/* Prepare array for rapid searching */
264
qsort(array, patterns, sizeof(struct netspec), netsort);
265
/* cure overlaps so that ranges are disjoint and consistent */
266
for (item=1; item<patterns; item++)
268
if (array[item].max <= array[item-1].max)
269
array[item] = array[item-1];
270
else if (array[item].min <= array[item-1].max)
271
array[item].min = array[item-1].max + 1; /* overflow possibility */
275
/* Match IPs from input stream to network patterns */
276
while (fgets(line, sizeof(line), inp_stream))
279
if ((key.min=ip_to_uint(line)))
282
if (bsearch(&key, array, patterns, sizeof(struct netspec), netsearch))
296
if (inp_stream != stdin)
302
printf("%u\n", counting-1);