]>
Commit | Line | Data |
---|---|---|
00196ec7 AM |
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 | ||
7743173d | 16 | |
00196ec7 | 17 | #include <ctype.h> |
7742e157 AF |
18 | #include <popt.h> |
19 | #include <stdio.h> | |
00196ec7 AM |
20 | #include <stdlib.h> |
21 | #include <string.h> | |
7742e157 | 22 | #include <sys/socket.h> |
00196ec7 | 23 | #include <sys/types.h> |
7742e157 AF |
24 | #include <netinet/in.h> |
25 | #include <arpa/inet.h> | |
26 | #include <netdb.h> | |
27 | ||
00196ec7 AM |
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)) | |
7742e157 | 38 | |
00196ec7 AM |
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 | /*! | |
52471a31 | 59 | \fn u_int32_t prefix2mask(int bits) |
00196ec7 AM |
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 | */ | |
52471a31 | 72 | u_int32_t prefix2mask(int prefix) { |
00196ec7 AM |
73 | return htonl(~((1 << (32 - prefix)) - 1)); |
74 | } | |
75 | ||
76 | /*! | |
52471a31 | 77 | \fn int mask2prefix(u_int32_t mask) |
00196ec7 AM |
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 | ||
52471a31 | 83 | \param mask is the netmask, specified as an u_int32_teger in network byte order. |
00196ec7 | 84 | \return the number of significant bits. */ |
52471a31 | 85 | int mask2prefix(u_int32_t mask) |
00196ec7 AM |
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 | /*! | |
52471a31 | 99 | \fn u_int32_t default_netmask(u_int32_t addr) |
00196ec7 AM |
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. */ | |
52471a31 | 111 | u_int32_t default_netmask(u_int32_t addr) |
00196ec7 AM |
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 | /*! | |
52471a31 | 122 | \fn u_int32_t calc_broadcast(u_int32_t addr, int prefix) |
00196ec7 AM |
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 | */ | |
52471a31 | 132 | u_int32_t calc_broadcast(u_int32_t addr, |
00196ec7 AM |
133 | int prefix) |
134 | { | |
135 | return (addr & prefix2mask(prefix)) | ~prefix2mask(prefix); | |
136 | } | |
137 | ||
138 | /*! | |
52471a31 | 139 | \fn u_int32_t calc_network(u_int32_t addr, int prefix) |
00196ec7 AM |
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 | */ | |
52471a31 | 147 | u_int32_t calc_network(u_int32_t addr, int prefix) |
00196ec7 AM |
148 | { |
149 | return (addr & prefix2mask(prefix)); | |
150 | } | |
151 | ||
152 | /*! | |
52471a31 | 153 | \fn const char *get_hostname(u_int32_t addr) |
00196ec7 AM |
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 | */ | |
52471a31 | 162 | const char *get_hostname(u_int32_t addr) |
00196ec7 AM |
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; | |
7742e157 AF |
189 | int beSilent = 0; |
190 | int rc; | |
191 | poptContext optCon; | |
00196ec7 AM |
192 | char *ipStr, *prefixStr, *netmaskStr, *hostName, *chptr; |
193 | struct in_addr ip, netmask, network, broadcast; | |
194 | int prefix = 0; | |
7742e157 AF |
195 | char errBuf[250]; |
196 | struct poptOption optionsTable[] = { | |
00196ec7 AM |
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 } | |
7742e157 AF |
211 | }; |
212 | ||
00196ec7 | 213 | optCon = poptGetContext("ipcalc", argc, argv, optionsTable, 0); |
7742e157 AF |
214 | poptReadDefaultConfig(optCon, 1); |
215 | ||
216 | if ((rc = poptGetNextOpt(optCon)) < -1) { | |
00196ec7 | 217 | if (!beSilent) { |
7742e157 AF |
218 | fprintf(stderr, "ipcalc: bad argument %s: %s\n", |
219 | poptBadOption(optCon, POPT_BADOPTION_NOALIAS), | |
220 | poptStrerror(rc)); | |
00196ec7 AM |
221 | poptPrintHelp(optCon, stderr, 0); |
222 | } | |
7742e157 AF |
223 | return 1; |
224 | } | |
225 | ||
00196ec7 AM |
226 | if (!(ipStr = (char *) poptGetArg(optCon))) { |
227 | if (!beSilent) { | |
7742e157 | 228 | fprintf(stderr, "ipcalc: ip address expected\n"); |
00196ec7 AM |
229 | poptPrintHelp(optCon, stderr, 0); |
230 | } | |
7742e157 AF |
231 | return 1; |
232 | } | |
233 | ||
00196ec7 AM |
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) { | |
7742e157 | 245 | if (!beSilent) |
00196ec7 AM |
246 | fprintf(stderr, "ipcalc: bad prefix: %s\n", |
247 | prefixStr); | |
7742e157 AF |
248 | return 1; |
249 | } | |
00196ec7 AM |
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 | } | |
7742e157 | 265 | return 1; |
00196ec7 AM |
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); | |
7742e157 AF |
274 | } |
275 | } | |
276 | ||
00196ec7 AM |
277 | if ((chptr = (char *) poptGetArg(optCon))) { |
278 | if (!beSilent) { | |
7742e157 | 279 | fprintf(stderr, "ipcalc: unexpected argument: %s\n", chptr); |
00196ec7 AM |
280 | poptPrintHelp(optCon, stderr, 0); |
281 | } | |
7742e157 AF |
282 | return 1; |
283 | } | |
284 | ||
00196ec7 AM |
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 | } | |
7742e157 AF |
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 | ||
00196ec7 AM |
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. */ | |
64e79ccb | 320 | |
7742e157 | 321 | if (showNetmask) { |
00196ec7 AM |
322 | if (prefix) { |
323 | netmask.s_addr = prefix2mask(prefix); | |
64e79ccb | 324 | } else { |
00196ec7 AM |
325 | netmask.s_addr = default_netmask(ip.s_addr); |
326 | prefix = mask2prefix(netmask.s_addr); | |
64e79ccb | 327 | } |
7742e157 | 328 | |
00196ec7 | 329 | printf("NETMASK=%s\n", inet_ntoa(netmask)); |
7742e157 AF |
330 | } |
331 | ||
00196ec7 AM |
332 | if (showPrefix) { |
333 | if (!prefix) | |
334 | prefix = mask2prefix(ip.s_addr); | |
335 | printf("PREFIX=%d\n", prefix); | |
336 | } | |
337 | ||
7742e157 | 338 | if (showBroadcast) { |
00196ec7 AM |
339 | broadcast.s_addr = calc_broadcast(ip.s_addr, prefix); |
340 | printf("BROADCAST=%s\n", inet_ntoa(broadcast)); | |
7742e157 AF |
341 | } |
342 | ||
343 | if (showNetwork) { | |
00196ec7 AM |
344 | network.s_addr = calc_network(ip.s_addr, prefix); |
345 | printf("NETWORK=%s\n", inet_ntoa(network)); | |
7742e157 | 346 | } |
00196ec7 AM |
347 | |
348 | if (showHostname) { | |
349 | if ((hostName = (char *) get_hostname(ip.s_addr)) == NULL) { | |
7742e157 AF |
350 | if (!beSilent) { |
351 | sprintf(errBuf, "ipcalc: cannot find hostname for %s", ipStr); | |
352 | herror(errBuf); | |
353 | } | |
7742e157 AF |
354 | return 1; |
355 | } | |
00196ec7 AM |
356 | |
357 | printf("HOSTNAME=%s\n", hostName); | |
7742e157 AF |
358 | } |
359 | ||
7742e157 AF |
360 | return 0; |
361 | } |