From: Michael Tremer Date: Sat, 28 May 2011 23:19:37 +0000 (+0200) Subject: initscripts: Add ipcalc binary. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fdd45d973090cc72efd6db1cad8143e775d4079f;p=ipfire-3.x.git initscripts: Add ipcalc binary. --- diff --git a/pkgs/initscripts/initscripts.nm b/pkgs/initscripts/initscripts.nm index de10dec61..6fef2055a 100644 --- a/pkgs/initscripts/initscripts.nm +++ b/pkgs/initscripts/initscripts.nm @@ -27,7 +27,7 @@ include $(PKGROOT)/Include PKG_NAME = initscripts PKG_EPOCH = 1 PKG_VER = 2.99 -PKG_REL = 3 +PKG_REL = 4 PKG_MAINTAINER = PKG_GROUPS = Base System/Boot @@ -35,7 +35,7 @@ PKG_URL = PKG_LICENSE = GPLv3+ PKG_SUMMARY = The set of scripts that initalize the system. -PKG_BUILD_DEPS+= glib2-devel +PKG_BUILD_DEPS+= glib2-devel popt-devel PKG_DEPS += bash coreutils e2fsprogs grep iproute2 module-init-tools \ procps sed system-release udev util-linux @@ -54,6 +54,10 @@ define STAGE_BUILD cd $(DIR_APP)/src && make $(PARALLELISMFLAGS) endef +define STAGE_TEST + cd $(DIR_APP)/src && make test +endef + define STAGE_INSTALL cd $(DIR_APP)/src && make install clean DESTDIR=$(BUILDROOT) diff --git a/pkgs/initscripts/src/Makefile b/pkgs/initscripts/src/Makefile index a93390de2..bb486faa3 100644 --- a/pkgs/initscripts/src/Makefile +++ b/pkgs/initscripts/src/Makefile @@ -1,5 +1,5 @@ -PROGS = console_check console_init securetty +PROGS = console_check console_init ipcalc securetty CC = gcc CFLAGS += -D_GNU_SOURCE $(shell pkg-config --cflags glib-2.0) @@ -9,17 +9,29 @@ all: $(PROGS) clean: rm -vf $(PROGS) *.o -install: - -mkdir -pv $(DESTDIR)/lib/udev $(DESTDIR)/sbin +test: ipcalc + ./ipcalc-tests + +install: $(PROGS) + # Install binaries + -mkdir -pv $(DESTDIR)/lib/udev $(DESTDIR)/{,s}bin install -v -m 755 console_check $(DESTDIR)/lib/udev/ install -v -m 755 console_init $(DESTDIR)/lib/udev/ + install -v -m 755 ipcalc $(DESTDIR)/bin install -v -m 755 securetty $(DESTDIR)/sbin + # Install man pages + -mkdir -pv $(DESTDIR)/usr/share/man/man1 + install -v -m 644 ipcalc.1 $(DESTDIR)/usr/share/man/man1 + console_check: console_check.o $(CC) $(LDFLAGS) -o $@ $< console_init: console_init.o shvar.o $(CC) $(LDFLAGS) $(shell pkg-config --libs glib-2.0) -o $@ $? +ipcalc: ipcalc.o + $(CC) $(LDFLAGS) -o $@ $< -lpopt + securetty: securetty.o $(CC) $(LDFLAGS) -o $@ $< diff --git a/pkgs/initscripts/src/ipcalc-tests b/pkgs/initscripts/src/ipcalc-tests new file mode 100755 index 000000000..ebb777d3a --- /dev/null +++ b/pkgs/initscripts/src/ipcalc-tests @@ -0,0 +1,98 @@ +#!/bin/bash +# +# Run some simple ipcalc tests. +# +# Adapted from: Matej Susta +# +# Copyright (c) 2009 Red Hat, Inc. All rights reserved. +# +# This copyrighted material is made available to anyone wishing +# to use, modify, copy, or redistribute it subject to the terms +# and conditions of the GNU General Public License version 2. +# +# This program is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this program; if not, write to the Free +# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. + +bin=./ipcalc +exitcode=0 + +ok() { + echo "ok." +} + +fail() { + echo "FAILED!" + exitcode=$((exitcode+1)) + echo -e "Output was:\n$output" +} + +TestSuccess() { + echo -n "Checking $@... " + output=$(sh -c "$1" 2>&1) + rc=$? + [ $rc -eq 0 ] && ok || fail $output +} + +TestFailure() { + echo -n "Checking $@... " + output=$(sh -c "$1" 2>&1) + rc=$? + [ $rc -eq 0 ] && fail $output || ok +} + +TestOutput() { + echo -n "Checking $1... " + output=$(sh -c "$1" 2>&1) + rc=$? + [ "$output" = "$2" ] && ok || fail $output +} + +echo -n "Checking for ipcalc binary... " +[ -x $bin ] || { fail ; exit $exitcode ; } + +TestSuccess "$bin --help" + +TestSuccess "$bin -c 127.0.0.1" +TestSuccess "$bin -c -6 ::1" +TestSuccess "$bin -c ::1" + +TestSuccess "$bin -c 192.168.1.1" +TestSuccess "$bin -c -6 2a01:198:200:300::2" +TestSuccess "$bin -c -6 2a01:198:200:300:0000:0000:0000:2" +TestSuccess "$bin -c -6 2a01:0198:0200:0300:0000:0000:0000:0002" +TestSuccess "$bin -c -6 ::1/128" +TestSuccess "$bin -c -6 fec0::1:0:0:c0a8:8002/64" + +TestFailure "$bin -c -6 gggg::" +TestFailure "$bin -b -6 ::1/128" +TestFailure "$bin -c -4 -6 2a01:198:200:300:0000:0000:0000:2" +TestFailure "$bin -c -4 -6 127.0.0.1" +TestFailure "$bin -c -6 127.0.0.1" +TestFailure "$bin -c ::1/999" +TestFailure "$bin -m 192.168.1.1/-1" +TestFailure "$bin -m 192.168.1.1/64" +TestFailure "$bin -m 192.168.1.1/99999" + +TestOutput "$bin -b 192.168.1.1/24" "BROADCAST=192.168.1.255" +TestOutput "$bin -b 192.168.1.1 255.255.255.0" "BROADCAST=192.168.1.255" +TestOutput "$bin -m 192.168.1.1/24" "NETMASK=255.255.255.0" +TestOutput "$bin -p 192.168.1.1 255.255.255.0" "PREFIX=24" +TestOutput "$bin -p 192.168.1.1 255.255.255.255" "PREFIX=32" +TestOutput "$bin -p 192.168.1.1 0.0.0.0" "PREFIX=0" +TestOutput "$bin -p 172.16.59.222 255.255.252.0" "PREFIX=22" +TestOutput "$bin -m 172.16.59.222/22" "NETMASK=255.255.252.0" +TestOutput "$bin -m 192.168.1.1" "NETMASK=255.255.255.0" +TestOutput "$bin -m 10.1.2.3" "NETMASK=255.0.0.0" +TestOutput "$bin -m 129.22.4.3" "NETMASK=255.255.0.0" +TestOutput "$bin -n 192.168.1.1/32" "NETWORK=192.168.1.1" +TestOutput "$bin -n 192.168.1.1/0" "NETWORK=0.0.0.0" + +echo "$exitcode test(s) failed." +exit $exitcode diff --git a/pkgs/initscripts/src/ipcalc.1 b/pkgs/initscripts/src/ipcalc.1 new file mode 100644 index 000000000..30e0b20dc --- /dev/null +++ b/pkgs/initscripts/src/ipcalc.1 @@ -0,0 +1,73 @@ +.TH IPCALC 1 "April 30 2001" "Red Hat, Inc." \" -*- nroff -*- +.SH NAME +ipcalc \- perform simple manipulation of IP addresses +.SH SYNOPSIS +.B ipcalc +[\fIOPTION\fR]... <\fBIP address\fR>[\fI/prefix\fR] [\fInetmask\fR] + +.SH DESCRIPTION +\fBipcalc\fR provides a simple way to calculate IP information for a host. +The various options specify what information \fBipcalc\fR should display +on standard out. Multiple options may be specified. An IP address to +operate on must always be specified. Most operations also require a +netmask or a CIDR prefix as well. + +.SH OPTIONS +.TP +.TP +\fB\-c\fR, \fB\-\-check\fR +Validate the IP address under the specified family. If no address +family is specified, IPv4 is assumed. + +.TP +\fB\-4\fR, \fB\-\-ipv4\fR +Specify IPv4 address family (default). + +.TP +\fB\-6\fR, \fB\-\-ipv6\fR +Specify IPv6 address family. + +.TP +\fB\-b\fR, \fB\-\-broadcast\fR +Display the broadcast address for the given IP address and netmask. + +.TP +\fB\-h\fR, \fB\-\-hostname\fR +Display the hostname for the given IP address. + +.TP +\fB\-m\fR, \fB\-\-netmask\fR +Calculate the netmask for the given IP address. It assumes that the IP +address is in a complete class A, B, or C network. Many networks do +not use the default netmasks, in which case an inappropriate value will +be returned. + +.TP +\fB\-p\fR, \fB\-\-prefix\fR +Show the prefix for the given mask/IP address. + +.TP +\fB\-n\fR, \fB\-\-network\fR +Display the network address for the given IP address and netmask. + +.TP +\fB\-s\fR, \fB\-\-silent\fR +Don't ever display error messages. + +.SH AUTHORS +.nf +Erik Troan +.nf +Preston Brown + +.TP +IPv6 supported wedged in by David Cantrell +.fi +.SH "REPORTING BUGS" +Report bugs at http://bugzilla.redhat.com/ +.SH COPYRIGHT +Copyright \(co 1997-2008 Red Hat, Inc. +.br +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. diff --git a/pkgs/initscripts/src/ipcalc.c b/pkgs/initscripts/src/ipcalc.c new file mode 100644 index 000000000..8decccadc --- /dev/null +++ b/pkgs/initscripts/src/ipcalc.c @@ -0,0 +1,476 @@ +/* + * Copyright (c) 1997-2009 Red Hat, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * Authors: + * Erik Troan + * Preston Brown + * David Cantrell + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*! + \file ipcalc.c + \brief provides utilities for manipulating IP addresses. + + ipcalc provides utilities and a front-end command line interface for + manipulating IP addresses, and calculating various aspects of an ip + address/netmask/network address/prefix/etc. + + Functionality can be accessed from other languages from the library + interface, documented here. To use ipcalc from the shell, read the + ipcalc(1) manual page. + + When passing parameters to the various functions, take note of whether they + take host byte order or network byte order. Most take host byte order, and + return host byte order, but there are some exceptions. +*/ + +/*! + \fn struct in_addr prefix2mask(int bits) + \brief creates a netmask from a specified number of bits + + This function converts a prefix length to a netmask. As CIDR (classless + internet domain internet domain routing) has taken off, more an more IP + addresses are being specified in the format address/prefix + (i.e. 192.168.2.3/24, with a corresponding netmask 255.255.255.0). If you + need to see what netmask corresponds to the prefix part of the address, this + is the function. See also \ref mask2prefix. + + \param prefix is the number of bits to create a mask for. + \return a network mask, in network byte order. +*/ +struct in_addr prefix2mask(int prefix) { + struct in_addr mask; + memset(&mask, 0, sizeof(mask)); + if (prefix) { + mask.s_addr = htonl(~((1 << (32 - prefix)) - 1)); + } else { + mask.s_addr = htonl(0); + } + return mask; +} + +/*! + \fn int mask2prefix(struct in_addr mask) + \brief calculates the number of bits masked off by a netmask. + + This function calculates the significant bits in an IP address as specified by + a netmask. See also \ref prefix2mask. + + \param mask is the netmask, specified as an struct in_addr in network byte order. + \return the number of significant bits. */ +int mask2prefix(struct in_addr mask) +{ + int count; + uint32_t saddr = ntohl(mask.s_addr); + + for (count=0; saddr > 0; count++) { + saddr=saddr << 1; + } + + return count; +} + +/*! + \fn struct in_addr default_netmask(struct in_addr addr) + + \brief returns the default (canonical) netmask associated with specified IP + address. + + When the Internet was originally set up, various ranges of IP addresses were + segmented into three network classes: A, B, and C. This function will return + a netmask that is associated with the IP address specified defining where it + falls in the predefined classes. + + \param addr an IP address in network byte order. + \return a netmask in network byte order. */ +struct in_addr default_netmask(struct in_addr addr) +{ + uint32_t saddr = addr.s_addr; + struct in_addr mask; + + memset(&mask, 0, sizeof(mask)); + + if (((ntohl(saddr) & 0xFF000000) >> 24) <= 127) + mask.s_addr = htonl(0xFF000000); + else if (((ntohl(saddr) & 0xFF000000) >> 24) <= 191) + mask.s_addr = htonl(0xFFFF0000); + else + mask.s_addr = htonl(0xFFFFFF00); + + return mask; +} + +/*! + \fn struct in_addr calc_broadcast(struct in_addr addr, int prefix) + + \brief calculate broadcast address given an IP address and a prefix length. + + \param addr an IP address in network byte order. + \param prefix a prefix length. + + \return the calculated broadcast address for the network, in network byte + order. +*/ +struct in_addr calc_broadcast(struct in_addr addr, int prefix) +{ + struct in_addr mask = prefix2mask(prefix); + struct in_addr broadcast; + + memset(&broadcast, 0, sizeof(broadcast)); + broadcast.s_addr = (addr.s_addr & mask.s_addr) | ~mask.s_addr; + return broadcast; +} + +/*! + \fn struct in_addr calc_network(struct in_addr addr, int prefix) + \brief calculates the network address for a specified address and prefix. + + \param addr an IP address, in network byte order + \param prefix the network prefix + \return the base address of the network that addr is associated with, in + network byte order. +*/ +struct in_addr calc_network(struct in_addr addr, int prefix) +{ + struct in_addr mask = prefix2mask(prefix); + struct in_addr network; + + memset(&network, 0, sizeof(network)); + network.s_addr = addr.s_addr & mask.s_addr; + return network; +} + +/*! + \fn const char *get_hostname(int family, void *addr) + \brief returns the hostname associated with the specified IP address + + \param family the address family, either AF_INET or AF_INET6. + \param addr an IP address to find a hostname for, in network byte order, + should either be a pointer to a struct in_addr or a struct in6_addr. + + \return a hostname, or NULL if one cannot be determined. Hostname is stored + in a static buffer that may disappear at any time, the caller should copy the + data if it needs permanent storage. +*/ +char *get_hostname(int family, void *addr) +{ + struct hostent * hostinfo = NULL; + int x; + struct in_addr addr4; + struct in6_addr addr6; + + if (family == AF_INET) { + memset(&addr4, 0, sizeof(addr4)); + memcpy(&addr4, addr, sizeof(addr4)); + hostinfo = gethostbyaddr((const void *) &addr4, + sizeof(addr4), family); + } else if (family == AF_INET6) { + memset(&addr6, 0, sizeof(addr6)); + memcpy(&addr6, addr, sizeof(addr6)); + hostinfo = gethostbyaddr((const void *) &addr6, + sizeof(addr6), family); + } + + if (!hostinfo) + return NULL; + + for (x=0; hostinfo->h_name[x]; x++) { + hostinfo->h_name[x] = tolower(hostinfo->h_name[x]); + } + return hostinfo->h_name; +} + +/*! + \fn main(int argc, const char **argv) + \brief wrapper program for ipcalc functions. + + This is a wrapper program for the functions that the ipcalc library provides. + It can be used from shell scripts or directly from the command line. + + For more information, please see the ipcalc(1) man page. +*/ +int main(int argc, const char **argv) { + int showBroadcast = 0, showPrefix = 0, showNetwork = 0; + int showHostname = 0, showNetmask = 0; + int beSilent = 0; + int doCheck = 0, familyIPv4 = 0, familyIPv6 = 0; + int rc; + poptContext optCon; + char *ipStr, *prefixStr, *netmaskStr, *chptr; + char *hostName = NULL; + char namebuf[INET6_ADDRSTRLEN+1]; + struct in_addr ip, netmask, network, broadcast; + struct in6_addr ip6; + int prefix = -1; + char errBuf[250]; + struct poptOption optionsTable[] = { + { "check", 'c', 0, &doCheck, 0, + "Validate IP address for specified address family", }, + { "ipv4", '4', 0, &familyIPv4, 0, + "IPv4 address family (default)", }, + { "ipv6", '6', 0, &familyIPv6, 0, + "IPv6 address family", }, + { "broadcast", 'b', 0, &showBroadcast, 0, + "Display calculated broadcast address", }, + { "hostname", 'h', 0, &showHostname, 0, + "Show hostname determined via DNS" }, + { "netmask", 'm', 0, &showNetmask, 0, + "Display default netmask for IP (class A, B, or C)" }, + { "network", 'n', 0, &showNetwork, 0, + "Display network address", }, + { "prefix", 'p', 0, &showPrefix, 0, + "Display network prefix", }, + { "silent", 's', 0, &beSilent, 0, + "Don't ever display error messages" }, + POPT_AUTOHELP + { NULL, '\0', 0, 0, 0, NULL, NULL } + }; + + optCon = poptGetContext("ipcalc", argc, argv, optionsTable, 0); + poptReadDefaultConfig(optCon, 1); + + if ((rc = poptGetNextOpt(optCon)) < -1) { + if (!beSilent) { + fprintf(stderr, "ipcalc: bad argument %s: %s\n", + poptBadOption(optCon, POPT_BADOPTION_NOALIAS), + poptStrerror(rc)); + poptPrintHelp(optCon, stderr, 0); + } + return 1; + } + + if (!(ipStr = (char *) poptGetArg(optCon))) { + if (!beSilent) { + fprintf(stderr, "ipcalc: ip address expected\n"); + poptPrintHelp(optCon, stderr, 0); + } + return 1; + } + + /* if there is a : in the address, it is an IPv6 address */ + if (strchr(ipStr,':') != NULL) { + familyIPv6=1; + } + + if (strchr(ipStr,'/') != NULL) { + prefixStr = strchr(ipStr, '/') + 1; + prefixStr--; + *prefixStr = '\0'; /* fix up ipStr */ + prefixStr++; + } else { + prefixStr = NULL; + } + + if (prefixStr != NULL) { + prefix = atoi(prefixStr); + if (prefix < 0 || ((familyIPv6 && prefix > 128) || (!familyIPv6 && prefix > 32))) { + if (!beSilent) + fprintf(stderr, "ipcalc: bad prefix: %s\n", prefixStr); + return 1; + } + } + + if (showBroadcast || showNetwork || showPrefix) { + if (!(netmaskStr = (char *) poptGetArg(optCon)) && (prefix < 0)) { + if (!beSilent) { + fprintf(stderr, "ipcalc: netmask or prefix expected\n"); + poptPrintHelp(optCon, stderr, 0); + } + return 1; + } else if (netmaskStr && prefix >= 0) { + if (!beSilent) { + fprintf(stderr, "ipcalc: both netmask and prefix specified\n"); + poptPrintHelp(optCon, stderr, 0); + } + return 1; + } else if (netmaskStr) { + if (inet_pton(AF_INET, netmaskStr, &netmask) <= 0) { + if (!beSilent) + fprintf(stderr, "ipcalc: bad netmask: %s\n", netmaskStr); + return 1; + } + prefix = mask2prefix(netmask); + } + } + + if ((chptr = (char *) poptGetArg(optCon))) { + if (!beSilent) { + fprintf(stderr, "ipcalc: unexpected argument: %s\n", chptr); + poptPrintHelp(optCon, stderr, 0); + } + return 1; + } + + if (!familyIPv4 && !familyIPv6) + familyIPv4 = 1; + + if (familyIPv4 && familyIPv6) { + if (!beSilent) { + fprintf(stderr, "ipcalc: cannot specify both address families\n"); + } + return 1; + } + + /* Handle CIDR entries such as 172/8 */ + if (prefix >= 0 && familyIPv4) { + char *tmp = ipStr; + int i; + + for (i=3; i> 0; i--) { + tmp = strchr(tmp,'.'); + if (!tmp) + break; + else + tmp++; + } + + tmp = NULL; + for (; i>0; i--) { + if (asprintf(&tmp, "%s.0", ipStr) == -1) { + fprintf(stderr, "Memory allocation failure line %d\n", __LINE__); + abort(); + } + ipStr = tmp; + } + } + + if (familyIPv4) { + if (inet_pton(AF_INET, ipStr, &ip) <= 0) { + if (!beSilent) + fprintf(stderr, "ipcalc: bad IPv4 address: %s\n", ipStr); + return 1; + } else if (prefix > 32) { + if (!beSilent) + fprintf(stderr, "ipcalc: bad IPv4 prefix %d\n", prefix); + return 1; + } else { + if (doCheck) + return 0; + } + } + + if (familyIPv6) { + if (inet_pton(AF_INET6, ipStr, &ip6) <= 0) { + if (!beSilent) + fprintf(stderr, "ipcalc: bad IPv6 address: %s\n", ipStr); + return 1; + } else if (prefix > 128) { + if (!beSilent) + fprintf(stderr, "ipcalc: bad IPv6 prefix %d\n", prefix); + return 1; + } else { + if (doCheck) + return 0; + } + } + + if (familyIPv6 && + (showBroadcast || showNetmask || showNetwork || showPrefix)) { + if (!beSilent) { + fprintf(stderr, "ipcalc: unable to show setting for IPv6\n"); + } + return 1; + } + + if (familyIPv4 && + !(showNetmask|showPrefix|showBroadcast|showNetwork|showHostname)) { + poptPrintHelp(optCon, stderr, 0); + return 1; + } + + poptFreeContext(optCon); + + /* we know what we want to display now, so display it. */ + + if (showNetmask) { + if (prefix >= 0) { + netmask = prefix2mask(prefix); + } else { + netmask = default_netmask(ip); + prefix = mask2prefix(netmask); + } + + memset(&namebuf, '\0', sizeof(namebuf)); + + if (inet_ntop(AF_INET, &netmask, namebuf, INET_ADDRSTRLEN) == NULL) { + fprintf(stderr, "Memory allocation failure line %d\n", __LINE__); + abort(); + } + + printf("NETMASK=%s\n", namebuf); + } + + if (showPrefix) { + if (prefix == -1) + prefix = mask2prefix(ip); + printf("PREFIX=%d\n", prefix); + } + + if (showBroadcast) { + broadcast = calc_broadcast(ip, prefix); + memset(&namebuf, '\0', sizeof(namebuf)); + + if (inet_ntop(AF_INET, &broadcast, namebuf, INET_ADDRSTRLEN) == NULL) { + fprintf(stderr, "Memory allocation failure line %d\n", __LINE__); + abort(); + } + + printf("BROADCAST=%s\n", namebuf); + } + + if (showNetwork) { + network = calc_network(ip, prefix); + memset(&namebuf, '\0', sizeof(namebuf)); + + if (inet_ntop(AF_INET, &network, namebuf, INET_ADDRSTRLEN) == NULL) { + fprintf(stderr, "Memory allocation failure line %d\n", __LINE__); + abort(); + } + + printf("NETWORK=%s\n", namebuf); + } + + if (showHostname) { + if (familyIPv4) { + hostName = get_hostname(AF_INET, &ip); + } else if (familyIPv6) { + hostName = get_hostname(AF_INET6, &ip6); + } + + if (hostName == NULL) { + if (!beSilent) { + sprintf(errBuf, "ipcalc: cannot find hostname for %s", ipStr); + herror(errBuf); + } + return 1; + } + + printf("HOSTNAME=%s\n", hostName); + } + + return 0; +}