/*
* NetLabel Control Utility, netlabelctl
*
* Author: Paul Moore <paul@paul-moore.com>
*
*/
/*
* (c) Copyright Hewlett-Packard Development Company, L.P., 2006
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License 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, see <http://www.gnu.org/licenses/>.
*
*/
#include <configure.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <getopt.h>
#include <arpa/inet.h>
#include <libnetlabel.h>
#include "netlabelctl.h"
/* return values */
#define RET_OK 0
#define RET_ERR 1
#define RET_USAGE 2
/* option variables */
uint32_t opt_verbose = 0;
uint32_t opt_timeout = 10;
uint32_t opt_pretty = 0;
/* program name */
char *nlctl_name = NULL;
/**
* Display usage information
* @param fp the output file pointer
*
* Display brief usage information.
*
*/
static void nlctl_usage_print(FILE *fp)
{
fprintf(fp, "usage: %s [<flags>] <module> [<commands>]\n", nlctl_name);
}
/**
* Display version information
* @param fp the output file pointer
*
* Display the version string.
*
*/
static void nlctl_ver_print(FILE *fp)
{
fprintf(fp, "NetLabel Control Utility, version %s\n", VERSION);
}
/**
* Display help information
* @param fp the output file pointer
*
* Display help and usage information.
*
*/
static void nlctl_help_print(FILE *fp)
{
nlctl_ver_print(fp);
fprintf(fp,
" Usage: %s [<flags>] <module> [<commands>]\n"
"\n"
" Flags:\n"
" -h : help/usage message\n"
" -p : make the output pretty\n"
" -t <secs> : timeout\n"
" -v : verbose mode\n"
"\n"
" Modules and Commands:\n"
" mgmt : NetLabel management\n"
" version\n"
" protocols\n"
" map : Domain/Protocol mapping\n"
" add default|domain:<domain> [address:<ADDR>[/<MASK>]]\n"
" protocol:<protocol>[,<extra>]\n"
" del default|domain:<domain>\n"
" list\n"
" unlbl : Unlabeled packet handling\n"
" accept on|off\n"
" add default|interface:<DEV> address:<ADDR>[/<MASK>]\n"
" label:<LABEL>\n"
" del default|interface:<DEV> address:<ADDR>[/<MASK>]\n"
" list\n"
" cipso|cipsov4 : CIPSO/IPv4 packet handling\n"
" add trans doi:<DOI> tags:<T1>,<Tn>\n"
" levels:<LL1>=<RL1>,<LLn>=<RLn>\n"
" categories:<LC1>=<RC1>,<LCn>=<RCn>\n"
" add pass doi:<DOI> tags:<T1>,<Tn>\n"
" add local doi:<DOI>\n"
" del doi:<DOI>\n"
" list [doi:<DOI>]\n"
" calipso : CALIPSO/IPv6 packet handling\n"
" add pass doi:<DOI>\n"
" del doi:<DOI>\n"
" list [doi:<DOI>]\n"
"\n",
nlctl_name);
}
/**
* Convert a errno value into a human readable string
* @param rc the errno return value
*
* Return a pointer to a human readable string describing the error in
* @rc.
*
*/
static char *nlctl_strerror(int rc)
{
char *str = NULL;
switch (rc) {
case 0:
str = "operation succeeded";
break;
case EINVAL:
str = "invalid argument or parameter";
break;
case ENOMEM:
str = "out of memory";
break;
case ENOENT:
str = "entry does not exist";
break;
case ENODATA:
str = "no data was available";
break;
case EBADMSG:
str = "bad message";
break;
case ENOPROTOOPT:
str = "not supported";
break;
case EAGAIN:
str = "try again";
break;
case ENOMSG:
str = "no message was received";
break;
default:
str = strerror(rc);
}
return str;
}
/**
* Display a network address
* @param addr the IP address to display
*
* Print the IP address and mask, specified in @addr, to STDIO.
*
*/
void nlctl_addr_print(const struct nlbl_netaddr *addr)
{
char addr_s[80];
socklen_t addr_s_len = 80;
struct in_addr mask4;
struct in6_addr mask6;
uint32_t mask_size;
uint32_t mask_off;
switch (addr->type) {
case AF_INET:
mask4.s_addr = ntohl(addr->mask.v4.s_addr);
for (mask_size = 0; mask4.s_addr != 0; mask_size++)
mask4.s_addr <<= 1;
printf("%s/%u",
inet_ntop(AF_INET, &addr->addr.v4, addr_s, addr_s_len),
mask_size);
break;
case AF_INET6:
for (mask_size = 0, mask_off = 0; mask_off < 4; mask_off++) {
mask6.s6_addr32[mask_off] =
ntohl(addr->mask.v6.s6_addr32[mask_off]);
while (mask6.s6_addr32[mask_off] != 0) {
mask_size++;
mask6.s6_addr32[mask_off] <<= 1;
}
}
printf("%s/%u",
inet_ntop(AF_INET6, &addr->addr.v6, addr_s, addr_s_len),
mask_size);
break;
default:
printf("UNKNOWN(%u)", addr->type);
break;
}
}
/**
* Parse an unsigned interger number
* @param str the number string
* @param num pointer to number to return
*
* Parse an unsigned integer number string and returns the value in @num.
* Returns zero on success, negative values on failure.
*
*/
static int _nlctl_num_parse(char *str, uint32_t *num)
{
char *spot = str;
while (*spot != '\0') {
if (*spot < '0' || *spot > '9')
return -EINVAL;
spot++;
}
*num = atoi(str);
return 0;
}
/**
* Parse a network address/mask pair
* @param addr_str the IP address/mask in string format
* @param addr the IP address/mask in native NetLabel format
*
* Parse the IP address/mask string into the given nlbl_netaddr structure.
* Returns zero on success, negative values on failure.
*
*/
int nlctl_addr_parse(char *addr_str, struct nlbl_netaddr *addr)
{
int rc;
char *mask;
uint32_t iter_a;
uint32_t iter_b;
/* sanity checks */
if (addr_str == NULL || addr_str[0] == '\0')
return -EINVAL;
/* separate the address mask */
mask = strstr(addr_str, "/");
if (mask != NULL) {
mask[0] = '\0';
mask++;
}
/* ipv4 */
rc = inet_pton(AF_INET, addr_str, &addr->addr.v4);
if (rc > 0) {
addr->type = AF_INET;
if (mask != NULL) {
rc = _nlctl_num_parse(mask, &iter_a);
if (rc < 0 || iter_a > 32)
return -EINVAL;
} else
iter_a = 32;
for (; iter_a > 0; iter_a--) {
addr->mask.v4.s_addr >>= 1;
addr->mask.v4.s_addr |= 0x80000000;
}
addr->mask.v4.s_addr = htonl(addr->mask.v4.s_addr);
return 0;
}
/* ipv6 */
rc = inet_pton(AF_INET6, addr_str, &addr->addr.v6);
if (rc > 0) {
addr->type = AF_INET6;
if (mask != NULL) {
rc = _nlctl_num_parse(mask, &iter_a);
if (rc < 0 || iter_a > 128)
return -EINVAL;
} else
iter_a = 128;
for (iter_b = 0; iter_a > 0 && iter_b < 4; iter_b++) {
for (; iter_a > 0 &&
addr->mask.v6.s6_addr32[iter_b] < 0xffffffff;
iter_a--) {
addr->mask.v6.s6_addr32[iter_b] >>= 1;
addr->mask.v6.s6_addr32[iter_b] |= 0x80000000;
}
addr->mask.v6.s6_addr32[iter_b] =
htonl(addr->mask.v6.s6_addr32[iter_b]);
}
return 0;
}
return -EINVAL;
}
/*
* main
*/
int main(int argc, char *argv[])
{
int rc = RET_ERR;
int arg_iter;
main_function_t *module_main = NULL;
char *module_name;
/* save the invoked program name for use in user notifications */
nlctl_name = strrchr(argv[0], '/');
if (nlctl_name == NULL)
nlctl_name = argv[0];
else if (nlctl_name[0] == '/')
nlctl_name += 1;
else
nlctl_name = strdup("unknown");
/* get the command line arguments and module information */
do {
arg_iter = getopt(argc, argv, "hvt:pV");
switch (arg_iter) {
case 'h':
/* help */
nlctl_help_print(stdout);
return RET_OK;
break;
case 'v':
/* verbose */
opt_verbose = 1;
break;
case 'p':
/* pretty */
opt_pretty = 1;
break;
case 't':
/* timeout */
if (atoi(optarg) < 0) {
nlctl_usage_print(stderr);
return RET_USAGE;
}
opt_timeout = atoi(optarg);
break;
case 'V':
/* version */
nlctl_ver_print(stdout);
return RET_OK;
break;
}
} while (arg_iter > 0);
module_name = argv[optind];
if (!module_name) {
nlctl_usage_print(stderr);
return RET_USAGE;
}
/* perform any setup we have to do */
rc = nlbl_init();
if (rc < 0) {
fprintf(stderr,
MSG_ERR("failed to initialize the NetLabel library\n"));
goto exit;
}
nlbl_comm_timeout(opt_timeout);
/* transfer control to the module */
if (!strcmp(module_name, "mgmt")) {
module_main = mgmt_main;
} else if (!strcmp(module_name, "map")) {
module_main = map_main;
} else if (!strcmp(module_name, "unlbl")) {
module_main = unlbl_main;
} else if (!strcmp(module_name, "cipsov4") ||
!strcmp(module_name, "cipso")) {
module_main = cipso_main;
} else if (!strcmp(module_name, "calipso")) {
module_main = calipso_main;
} else {
fprintf(stderr,
MSG_ERR("unknown or missing module '%s'\n"),
module_name);
rc = RET_ERR;
goto exit;
}
rc = module_main(argc - optind - 1, argv + optind + 1);
if (rc < 0) {
fprintf(stderr, MSG_ERR("%s\n"), nlctl_strerror(-rc));
rc = RET_ERR;
} else
rc = RET_OK;
exit:
nlbl_exit();
return rc;
}