]> git.ipfire.org Git - people/amarx/ipfire-3.x.git/blob - pkgs/initscripts/src/ipcalc.c
8decccadcb1a93870676867f0f79ccd913fe1db1
[people/amarx/ipfire-3.x.git] / pkgs / initscripts / src / ipcalc.c
1 /*
2 * Copyright (c) 1997-2009 Red Hat, Inc. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
16 *
17 * Authors:
18 * Erik Troan <ewt@redhat.com>
19 * Preston Brown <pbrown@redhat.com>
20 * David Cantrell <dcantrell@redhat.com>
21 */
22
23 #include <ctype.h>
24 #include <popt.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/socket.h>
29 #include <sys/types.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <netdb.h>
33
34 /*!
35 \file ipcalc.c
36 \brief provides utilities for manipulating IP addresses.
37
38 ipcalc provides utilities and a front-end command line interface for
39 manipulating IP addresses, and calculating various aspects of an ip
40 address/netmask/network address/prefix/etc.
41
42 Functionality can be accessed from other languages from the library
43 interface, documented here. To use ipcalc from the shell, read the
44 ipcalc(1) manual page.
45
46 When passing parameters to the various functions, take note of whether they
47 take host byte order or network byte order. Most take host byte order, and
48 return host byte order, but there are some exceptions.
49 */
50
51 /*!
52 \fn struct in_addr prefix2mask(int bits)
53 \brief creates a netmask from a specified number of bits
54
55 This function converts a prefix length to a netmask. As CIDR (classless
56 internet domain internet domain routing) has taken off, more an more IP
57 addresses are being specified in the format address/prefix
58 (i.e. 192.168.2.3/24, with a corresponding netmask 255.255.255.0). If you
59 need to see what netmask corresponds to the prefix part of the address, this
60 is the function. See also \ref mask2prefix.
61
62 \param prefix is the number of bits to create a mask for.
63 \return a network mask, in network byte order.
64 */
65 struct in_addr prefix2mask(int prefix) {
66 struct in_addr mask;
67 memset(&mask, 0, sizeof(mask));
68 if (prefix) {
69 mask.s_addr = htonl(~((1 << (32 - prefix)) - 1));
70 } else {
71 mask.s_addr = htonl(0);
72 }
73 return mask;
74 }
75
76 /*!
77 \fn int mask2prefix(struct in_addr mask)
78 \brief calculates the number of bits masked off by a netmask.
79
80 This function calculates the significant bits in an IP address as specified by
81 a netmask. See also \ref prefix2mask.
82
83 \param mask is the netmask, specified as an struct in_addr in network byte order.
84 \return the number of significant bits. */
85 int mask2prefix(struct in_addr mask)
86 {
87 int count;
88 uint32_t saddr = ntohl(mask.s_addr);
89
90 for (count=0; saddr > 0; count++) {
91 saddr=saddr << 1;
92 }
93
94 return count;
95 }
96
97 /*!
98 \fn struct in_addr default_netmask(struct in_addr addr)
99
100 \brief returns the default (canonical) netmask associated with specified IP
101 address.
102
103 When the Internet was originally set up, various ranges of IP addresses were
104 segmented into three network classes: A, B, and C. This function will return
105 a netmask that is associated with the IP address specified defining where it
106 falls in the predefined classes.
107
108 \param addr an IP address in network byte order.
109 \return a netmask in network byte order. */
110 struct in_addr default_netmask(struct in_addr addr)
111 {
112 uint32_t saddr = addr.s_addr;
113 struct in_addr mask;
114
115 memset(&mask, 0, sizeof(mask));
116
117 if (((ntohl(saddr) & 0xFF000000) >> 24) <= 127)
118 mask.s_addr = htonl(0xFF000000);
119 else if (((ntohl(saddr) & 0xFF000000) >> 24) <= 191)
120 mask.s_addr = htonl(0xFFFF0000);
121 else
122 mask.s_addr = htonl(0xFFFFFF00);
123
124 return mask;
125 }
126
127 /*!
128 \fn struct in_addr calc_broadcast(struct in_addr addr, int prefix)
129
130 \brief calculate broadcast address given an IP address and a prefix length.
131
132 \param addr an IP address in network byte order.
133 \param prefix a prefix length.
134
135 \return the calculated broadcast address for the network, in network byte
136 order.
137 */
138 struct in_addr calc_broadcast(struct in_addr addr, int prefix)
139 {
140 struct in_addr mask = prefix2mask(prefix);
141 struct in_addr broadcast;
142
143 memset(&broadcast, 0, sizeof(broadcast));
144 broadcast.s_addr = (addr.s_addr & mask.s_addr) | ~mask.s_addr;
145 return broadcast;
146 }
147
148 /*!
149 \fn struct in_addr calc_network(struct in_addr addr, int prefix)
150 \brief calculates the network address for a specified address and prefix.
151
152 \param addr an IP address, in network byte order
153 \param prefix the network prefix
154 \return the base address of the network that addr is associated with, in
155 network byte order.
156 */
157 struct in_addr calc_network(struct in_addr addr, int prefix)
158 {
159 struct in_addr mask = prefix2mask(prefix);
160 struct in_addr network;
161
162 memset(&network, 0, sizeof(network));
163 network.s_addr = addr.s_addr & mask.s_addr;
164 return network;
165 }
166
167 /*!
168 \fn const char *get_hostname(int family, void *addr)
169 \brief returns the hostname associated with the specified IP address
170
171 \param family the address family, either AF_INET or AF_INET6.
172 \param addr an IP address to find a hostname for, in network byte order,
173 should either be a pointer to a struct in_addr or a struct in6_addr.
174
175 \return a hostname, or NULL if one cannot be determined. Hostname is stored
176 in a static buffer that may disappear at any time, the caller should copy the
177 data if it needs permanent storage.
178 */
179 char *get_hostname(int family, void *addr)
180 {
181 struct hostent * hostinfo = NULL;
182 int x;
183 struct in_addr addr4;
184 struct in6_addr addr6;
185
186 if (family == AF_INET) {
187 memset(&addr4, 0, sizeof(addr4));
188 memcpy(&addr4, addr, sizeof(addr4));
189 hostinfo = gethostbyaddr((const void *) &addr4,
190 sizeof(addr4), family);
191 } else if (family == AF_INET6) {
192 memset(&addr6, 0, sizeof(addr6));
193 memcpy(&addr6, addr, sizeof(addr6));
194 hostinfo = gethostbyaddr((const void *) &addr6,
195 sizeof(addr6), family);
196 }
197
198 if (!hostinfo)
199 return NULL;
200
201 for (x=0; hostinfo->h_name[x]; x++) {
202 hostinfo->h_name[x] = tolower(hostinfo->h_name[x]);
203 }
204 return hostinfo->h_name;
205 }
206
207 /*!
208 \fn main(int argc, const char **argv)
209 \brief wrapper program for ipcalc functions.
210
211 This is a wrapper program for the functions that the ipcalc library provides.
212 It can be used from shell scripts or directly from the command line.
213
214 For more information, please see the ipcalc(1) man page.
215 */
216 int main(int argc, const char **argv) {
217 int showBroadcast = 0, showPrefix = 0, showNetwork = 0;
218 int showHostname = 0, showNetmask = 0;
219 int beSilent = 0;
220 int doCheck = 0, familyIPv4 = 0, familyIPv6 = 0;
221 int rc;
222 poptContext optCon;
223 char *ipStr, *prefixStr, *netmaskStr, *chptr;
224 char *hostName = NULL;
225 char namebuf[INET6_ADDRSTRLEN+1];
226 struct in_addr ip, netmask, network, broadcast;
227 struct in6_addr ip6;
228 int prefix = -1;
229 char errBuf[250];
230 struct poptOption optionsTable[] = {
231 { "check", 'c', 0, &doCheck, 0,
232 "Validate IP address for specified address family", },
233 { "ipv4", '4', 0, &familyIPv4, 0,
234 "IPv4 address family (default)", },
235 { "ipv6", '6', 0, &familyIPv6, 0,
236 "IPv6 address family", },
237 { "broadcast", 'b', 0, &showBroadcast, 0,
238 "Display calculated broadcast address", },
239 { "hostname", 'h', 0, &showHostname, 0,
240 "Show hostname determined via DNS" },
241 { "netmask", 'm', 0, &showNetmask, 0,
242 "Display default netmask for IP (class A, B, or C)" },
243 { "network", 'n', 0, &showNetwork, 0,
244 "Display network address", },
245 { "prefix", 'p', 0, &showPrefix, 0,
246 "Display network prefix", },
247 { "silent", 's', 0, &beSilent, 0,
248 "Don't ever display error messages" },
249 POPT_AUTOHELP
250 { NULL, '\0', 0, 0, 0, NULL, NULL }
251 };
252
253 optCon = poptGetContext("ipcalc", argc, argv, optionsTable, 0);
254 poptReadDefaultConfig(optCon, 1);
255
256 if ((rc = poptGetNextOpt(optCon)) < -1) {
257 if (!beSilent) {
258 fprintf(stderr, "ipcalc: bad argument %s: %s\n",
259 poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
260 poptStrerror(rc));
261 poptPrintHelp(optCon, stderr, 0);
262 }
263 return 1;
264 }
265
266 if (!(ipStr = (char *) poptGetArg(optCon))) {
267 if (!beSilent) {
268 fprintf(stderr, "ipcalc: ip address expected\n");
269 poptPrintHelp(optCon, stderr, 0);
270 }
271 return 1;
272 }
273
274 /* if there is a : in the address, it is an IPv6 address */
275 if (strchr(ipStr,':') != NULL) {
276 familyIPv6=1;
277 }
278
279 if (strchr(ipStr,'/') != NULL) {
280 prefixStr = strchr(ipStr, '/') + 1;
281 prefixStr--;
282 *prefixStr = '\0'; /* fix up ipStr */
283 prefixStr++;
284 } else {
285 prefixStr = NULL;
286 }
287
288 if (prefixStr != NULL) {
289 prefix = atoi(prefixStr);
290 if (prefix < 0 || ((familyIPv6 && prefix > 128) || (!familyIPv6 && prefix > 32))) {
291 if (!beSilent)
292 fprintf(stderr, "ipcalc: bad prefix: %s\n", prefixStr);
293 return 1;
294 }
295 }
296
297 if (showBroadcast || showNetwork || showPrefix) {
298 if (!(netmaskStr = (char *) poptGetArg(optCon)) && (prefix < 0)) {
299 if (!beSilent) {
300 fprintf(stderr, "ipcalc: netmask or prefix expected\n");
301 poptPrintHelp(optCon, stderr, 0);
302 }
303 return 1;
304 } else if (netmaskStr && prefix >= 0) {
305 if (!beSilent) {
306 fprintf(stderr, "ipcalc: both netmask and prefix specified\n");
307 poptPrintHelp(optCon, stderr, 0);
308 }
309 return 1;
310 } else if (netmaskStr) {
311 if (inet_pton(AF_INET, netmaskStr, &netmask) <= 0) {
312 if (!beSilent)
313 fprintf(stderr, "ipcalc: bad netmask: %s\n", netmaskStr);
314 return 1;
315 }
316 prefix = mask2prefix(netmask);
317 }
318 }
319
320 if ((chptr = (char *) poptGetArg(optCon))) {
321 if (!beSilent) {
322 fprintf(stderr, "ipcalc: unexpected argument: %s\n", chptr);
323 poptPrintHelp(optCon, stderr, 0);
324 }
325 return 1;
326 }
327
328 if (!familyIPv4 && !familyIPv6)
329 familyIPv4 = 1;
330
331 if (familyIPv4 && familyIPv6) {
332 if (!beSilent) {
333 fprintf(stderr, "ipcalc: cannot specify both address families\n");
334 }
335 return 1;
336 }
337
338 /* Handle CIDR entries such as 172/8 */
339 if (prefix >= 0 && familyIPv4) {
340 char *tmp = ipStr;
341 int i;
342
343 for (i=3; i> 0; i--) {
344 tmp = strchr(tmp,'.');
345 if (!tmp)
346 break;
347 else
348 tmp++;
349 }
350
351 tmp = NULL;
352 for (; i>0; i--) {
353 if (asprintf(&tmp, "%s.0", ipStr) == -1) {
354 fprintf(stderr, "Memory allocation failure line %d\n", __LINE__);
355 abort();
356 }
357 ipStr = tmp;
358 }
359 }
360
361 if (familyIPv4) {
362 if (inet_pton(AF_INET, ipStr, &ip) <= 0) {
363 if (!beSilent)
364 fprintf(stderr, "ipcalc: bad IPv4 address: %s\n", ipStr);
365 return 1;
366 } else if (prefix > 32) {
367 if (!beSilent)
368 fprintf(stderr, "ipcalc: bad IPv4 prefix %d\n", prefix);
369 return 1;
370 } else {
371 if (doCheck)
372 return 0;
373 }
374 }
375
376 if (familyIPv6) {
377 if (inet_pton(AF_INET6, ipStr, &ip6) <= 0) {
378 if (!beSilent)
379 fprintf(stderr, "ipcalc: bad IPv6 address: %s\n", ipStr);
380 return 1;
381 } else if (prefix > 128) {
382 if (!beSilent)
383 fprintf(stderr, "ipcalc: bad IPv6 prefix %d\n", prefix);
384 return 1;
385 } else {
386 if (doCheck)
387 return 0;
388 }
389 }
390
391 if (familyIPv6 &&
392 (showBroadcast || showNetmask || showNetwork || showPrefix)) {
393 if (!beSilent) {
394 fprintf(stderr, "ipcalc: unable to show setting for IPv6\n");
395 }
396 return 1;
397 }
398
399 if (familyIPv4 &&
400 !(showNetmask|showPrefix|showBroadcast|showNetwork|showHostname)) {
401 poptPrintHelp(optCon, stderr, 0);
402 return 1;
403 }
404
405 poptFreeContext(optCon);
406
407 /* we know what we want to display now, so display it. */
408
409 if (showNetmask) {
410 if (prefix >= 0) {
411 netmask = prefix2mask(prefix);
412 } else {
413 netmask = default_netmask(ip);
414 prefix = mask2prefix(netmask);
415 }
416
417 memset(&namebuf, '\0', sizeof(namebuf));
418
419 if (inet_ntop(AF_INET, &netmask, namebuf, INET_ADDRSTRLEN) == NULL) {
420 fprintf(stderr, "Memory allocation failure line %d\n", __LINE__);
421 abort();
422 }
423
424 printf("NETMASK=%s\n", namebuf);
425 }
426
427 if (showPrefix) {
428 if (prefix == -1)
429 prefix = mask2prefix(ip);
430 printf("PREFIX=%d\n", prefix);
431 }
432
433 if (showBroadcast) {
434 broadcast = calc_broadcast(ip, prefix);
435 memset(&namebuf, '\0', sizeof(namebuf));
436
437 if (inet_ntop(AF_INET, &broadcast, namebuf, INET_ADDRSTRLEN) == NULL) {
438 fprintf(stderr, "Memory allocation failure line %d\n", __LINE__);
439 abort();
440 }
441
442 printf("BROADCAST=%s\n", namebuf);
443 }
444
445 if (showNetwork) {
446 network = calc_network(ip, prefix);
447 memset(&namebuf, '\0', sizeof(namebuf));
448
449 if (inet_ntop(AF_INET, &network, namebuf, INET_ADDRSTRLEN) == NULL) {
450 fprintf(stderr, "Memory allocation failure line %d\n", __LINE__);
451 abort();
452 }
453
454 printf("NETWORK=%s\n", namebuf);
455 }
456
457 if (showHostname) {
458 if (familyIPv4) {
459 hostName = get_hostname(AF_INET, &ip);
460 } else if (familyIPv6) {
461 hostName = get_hostname(AF_INET6, &ip6);
462 }
463
464 if (hostName == NULL) {
465 if (!beSilent) {
466 sprintf(errBuf, "ipcalc: cannot find hostname for %s", ipStr);
467 herror(errBuf);
468 }
469 return 1;
470 }
471
472 printf("HOSTNAME=%s\n", hostName);
473 }
474
475 return 0;
476 }