]> TLD Linux GIT Repositories - rc-scripts.git/blob - src/ipcalc.c
- from PLD
[rc-scripts.git] / src / ipcalc.c
1 /*
2  * Copyright (c) 1997-2003 Red Hat, Inc. All rights reserved.
3  *
4  * This software may be freely redistributed under the terms of the GNU
5  * public license.
6  * 
7  * You should have received a copy of the GNU General Public License
8  * along with this program; if not, write to the Free Software
9  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
10  *
11  * Authors:
12  *   Erik Troan <ewt@redhat.com>
13  *   Preston Brown <pbrown@redhat.com>
14  */
15      
16
17 #include <ctype.h>
18 #include <popt.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <sys/socket.h>
23 #include <sys/types.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
26 #include <netdb.h>
27
28 /*!
29   \def IPBITS
30   \brief the number of bits in an IP address.
31 */
32 #define IPBITS (sizeof(u_int32_t) * 8)
33 /*!
34   \def IPBYTES
35   \brief the number of bytes in an IP address.
36 */
37 #define IPBYTES (sizeof(u_int32_t))
38
39
40 /*!
41   \file ipcalc.c
42   \brief provides utilities for manipulating IP addresses.
43
44   ipcalc provides utilities and a front-end command line interface for
45   manipulating IP addresses, and calculating various aspects of an ip
46   address/netmask/network address/prefix/etc.
47
48   Functionality can be accessed from other languages from the library
49   interface, documented here.  To use ipcalc from the shell, read the
50   ipcalc(1) manual page.
51
52   When passing parameters to the various functions, take note of whether they
53   take host byte order or network byte order.  Most take host byte order, and
54   return host byte order, but there are some exceptions.
55   
56 */
57
58 /*!
59   \fn u_int32_t prefix2mask(int bits)
60   \brief creates a netmask from a specified number of bits
61   
62   This function converts a prefix length to a netmask.  As CIDR (classless
63   internet domain internet domain routing) has taken off, more an more IP
64   addresses are being specified in the format address/prefix
65   (i.e. 192.168.2.3/24, with a corresponding netmask 255.255.255.0).  If you
66   need to see what netmask corresponds to the prefix part of the address, this
67   is the function.  See also \ref mask2prefix.
68   
69   \param prefix is the number of bits to create a mask for.
70   \return a network mask, in network byte order.
71 */
72 u_int32_t prefix2mask(int prefix) {
73     return htonl(~((1 << (32 - prefix)) - 1));
74 }
75
76 /*!
77   \fn int mask2prefix(u_int32_t 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 u_int32_teger in network byte order.
84   \return the number of significant bits.  */
85 int mask2prefix(u_int32_t mask)
86 {
87     int i;
88     int count = IPBITS;
89         
90     for (i = 0; i < IPBITS; i++) {
91         if (!(ntohl(mask) & ((2 << i) - 1)))
92             count--;
93     }
94
95     return count;
96 }
97
98 /*!
99   \fn u_int32_t default_netmask(u_int32_t addr)
100
101   \brief returns the default (canonical) netmask associated with specified IP
102   address.
103
104   When the Internet was originally set up, various ranges of IP addresses were
105   segmented into three network classes: A, B, and C.  This function will return
106   a netmask that is associated with the IP address specified defining where it
107   falls in the predefined classes.
108
109   \param addr an IP address in network byte order.
110   \return a netmask in network byte order.  */
111 u_int32_t default_netmask(u_int32_t addr)
112 {
113     if (((ntohl(addr) & 0xFF000000) >> 24) <= 127)
114         return htonl(0xFF000000);
115     else if (((ntohl(addr) & 0xFF000000) >> 24) <= 191)
116         return htonl(0xFFFF0000);
117     else
118         return htonl(0xFFFFFF00);
119 }
120
121 /*!
122   \fn u_int32_t calc_broadcast(u_int32_t addr, int prefix)
123
124   \brief calculate broadcast address given an IP address and a prefix length.
125
126   \param addr an IP address in network byte order.
127   \param prefix a prefix length.
128   
129   \return the calculated broadcast address for the network, in network byte
130   order.
131 */
132 u_int32_t calc_broadcast(u_int32_t addr,
133                                  int prefix)
134 {  
135     return (addr & prefix2mask(prefix)) | ~prefix2mask(prefix);
136 }
137
138 /*!
139   \fn u_int32_t calc_network(u_int32_t addr, int prefix)
140   \brief calculates the network address for a specified address and prefix.
141
142   \param addr an IP address, in network byte order
143   \param prefix the network prefix
144   \return the base address of the network that addr is associated with, in
145   network byte order.
146 */
147 u_int32_t calc_network(u_int32_t addr, int prefix)
148 {
149     return (addr & prefix2mask(prefix));
150 }
151
152 /*!
153   \fn const char *get_hostname(u_int32_t addr)
154   \brief returns the hostname associated with the specified IP address
155
156   \param addr an IP address to find a hostname for, in network byte order
157
158   \return a hostname, or NULL if one cannot be determined.  Hostname is stored
159   in a static buffer that may disappear at any time, the caller should copy the
160   data if it needs permanent storage.
161 */
162 const char *get_hostname(u_int32_t addr)
163 {
164     struct hostent * hostinfo;
165     int x;
166     
167     hostinfo = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET);
168     if (!hostinfo)
169         return NULL;
170
171     for (x=0; hostinfo->h_name[x]; x++) {
172         hostinfo->h_name[x] = tolower(hostinfo->h_name[x]);
173     }
174     return hostinfo->h_name;
175 }
176
177 /*!
178   \fn main(int argc, const char **argv)
179   \brief wrapper program for ipcalc functions.
180   
181   This is a wrapper program for the functions that the ipcalc library provides.
182   It can be used from shell scripts or directly from the command line.
183   
184   For more information, please see the ipcalc(1) man page.
185 */
186 int main(int argc, const char **argv) {
187     int showBroadcast = 0, showPrefix = 0, showNetwork = 0;
188     int showHostname = 0, showNetmask = 0;
189     int beSilent = 0;
190     int rc;
191     poptContext optCon;
192     char *ipStr, *prefixStr, *netmaskStr, *hostName, *chptr;
193     struct in_addr ip, netmask, network, broadcast;
194     int prefix = 0;
195     char errBuf[250];
196     struct poptOption optionsTable[] = {
197             { "broadcast", 'b', 0, &showBroadcast, 0,
198                 "Display calculated broadcast address", },
199             { "hostname", 'h', 0, &showHostname, 0,
200                 "Show hostname determined via DNS" },
201             { "netmask", 'm', 0, &showNetmask, 0,
202                 "Display default netmask for IP (class A, B, or C)" },
203             { "network", 'n', 0, &showNetwork, 0,
204                 "Display network address", },
205             { "prefix", 'p', 0, &showPrefix, 0,
206               "Display network prefix", },
207             { "silent", 's', 0, &beSilent, 0,
208                 "Don't ever display error messages " },
209             POPT_AUTOHELP
210             { NULL, '\0', 0, 0, 0, NULL, NULL }
211     };
212
213     optCon = poptGetContext("ipcalc", argc, argv, optionsTable, 0);
214     poptReadDefaultConfig(optCon, 1);
215
216     if ((rc = poptGetNextOpt(optCon)) < -1) {
217         if (!beSilent) {
218             fprintf(stderr, "ipcalc: bad argument %s: %s\n", 
219                     poptBadOption(optCon, POPT_BADOPTION_NOALIAS), 
220                     poptStrerror(rc));
221             poptPrintHelp(optCon, stderr, 0);
222         }
223         return 1;
224     }
225
226     if (!(ipStr = (char *) poptGetArg(optCon))) {
227         if (!beSilent) {
228             fprintf(stderr, "ipcalc: ip address expected\n");
229             poptPrintHelp(optCon, stderr, 0);
230         }
231         return 1;
232     }
233
234     if (strchr(ipStr,'/') != NULL) {
235         prefixStr = strchr(ipStr, '/') + 1;
236         prefixStr--;
237         *prefixStr = '\0';  /* fix up ipStr */
238         prefixStr++;
239     } else
240         prefixStr = NULL;
241     
242     if (prefixStr != NULL) {
243         prefix = atoi(prefixStr);
244         if (prefix == 0) {
245             if (!beSilent)
246                 fprintf(stderr, "ipcalc: bad prefix: %s\n",
247                         prefixStr);
248             return 1;
249         }
250     }
251         
252     if (showBroadcast || showNetwork || showPrefix) {
253         if (!(netmaskStr = (char *) poptGetArg(optCon)) &&
254             (prefix == 0)) {
255             if (!beSilent) {
256                 fprintf(stderr, "ipcalc: netmask or prefix expected\n");
257                 poptPrintHelp(optCon, stderr, 0);
258             }
259             return 1;
260         } else if (netmaskStr && prefix != 0) {
261             if (!beSilent) {
262                     fprintf(stderr, "ipcalc: both netmask and prefix specified\n");
263                     poptPrintHelp(optCon, stderr, 0);
264             }
265             return 1;
266         } else if (netmaskStr) {
267             if (!inet_aton(netmaskStr, &netmask)) {
268                 if (!beSilent)
269                     fprintf(stderr, "ipcalc: bad netmask: %s\n",
270                             netmaskStr);
271                 return 1;
272             }
273             prefix = mask2prefix(netmask.s_addr);
274         }
275     }
276
277     if ((chptr = (char *) poptGetArg(optCon))) {
278         if (!beSilent) {
279             fprintf(stderr, "ipcalc: unexpected argument: %s\n", chptr);
280             poptPrintHelp(optCon, stderr, 0);
281         }
282         return 1;
283     }
284
285     /* Handle CIDR entries such as 172/8 */
286     if (prefix) {
287         char *tmp = ipStr;
288         int i;
289             
290         for(i=3; i> 0; i--) {
291                 tmp = strchr(tmp,'.');
292                 if (!tmp) 
293                         break;
294                 else
295                         tmp++;
296         }
297         tmp = NULL;
298         for (; i>0; i--) {
299            tmp = malloc(strlen(ipStr) + 3);
300            sprintf(tmp,"%s.0",ipStr);
301            ipStr = tmp;
302         }
303     }
304
305     if (!inet_aton(ipStr, (struct in_addr *) &ip)) {
306         if (!beSilent)
307             fprintf(stderr, "ipcalc: bad ip address: %s\n", ipStr);
308         return 1;
309     }
310
311     
312     if (!(showNetmask|showPrefix|showBroadcast|showNetwork|showHostname)) {
313             poptPrintHelp(optCon, stderr, 0);
314             return 1;
315     }
316
317     poptFreeContext(optCon);
318
319     /* we know what we want to display now, so display it. */
320
321     if (showNetmask) {
322         if (prefix) {
323             netmask.s_addr = prefix2mask(prefix);
324         } else {
325             netmask.s_addr = default_netmask(ip.s_addr);
326             prefix = mask2prefix(netmask.s_addr);
327         }
328
329         printf("NETMASK=%s\n", inet_ntoa(netmask));
330     }
331
332     if (showPrefix) {
333         if (!prefix)
334             prefix = mask2prefix(ip.s_addr);
335         printf("PREFIX=%d\n", prefix);
336     }
337             
338     if (showBroadcast) {
339         broadcast.s_addr = calc_broadcast(ip.s_addr, prefix);
340         printf("BROADCAST=%s\n", inet_ntoa(broadcast));
341     }
342
343     if (showNetwork) {
344         network.s_addr = calc_network(ip.s_addr, prefix);
345         printf("NETWORK=%s\n", inet_ntoa(network));
346     }
347     
348     if (showHostname) { 
349         if ((hostName = (char *) get_hostname(ip.s_addr)) == NULL) {
350             if (!beSilent) {
351                 sprintf(errBuf, "ipcalc: cannot find hostname for %s", ipStr);
352                 herror(errBuf);
353             }
354             return 1;
355         }
356         
357         printf("HOSTNAME=%s\n", hostName);
358     }
359         
360     return 0;
361 }