/*
 * nbadmin.c - This file contains code of 'nbadmin' utility which is the major
 *             ADMINISTRATION utility for PROCOM NetBEUI.
 *
 * Usage:
 *	If it is run without arguments, it shows its usage form. The
 *	'NetInterfaceName' is name of network interface that administrator
 *	wants to "binds NetBEUI to" or "unbinds NetBEUI from" it.
 *	If during 'unbind' request some links exist at the interface, then
 *	system ignores the request unless the '-f' (force) switch is specified.
 *	The 'drop' request is used for dropping a specific NetBIOS session or
 *	a specific LLC link (connection) with all of its sessions. If
 *	'SessionNum' is specified, then that session at link with 'LinkNum'
 *	number will be dropped; Otherwise the link with all of its sessions will
 *	be dropped.
 *	This utility only can use by a superuser.
 *
 * Copyright (c) 1997 by Procom Technology,Inc.
 *
 * This program can be redistributed or modified under the terms of the 
 * GNU General Public License as published by the Free Software Foundation.
 * This program is distributed without any warranty or implied warranty
 * of merchantability or fitness for a particular purpose.
 *
 * See the GNU General Public License for more details.
 *
 */
 

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <linux/af_netb.h>


/*
 * Function: do_bind
 *	Binds NetBEUI protocol stack to a specified network interface.
 *
 * Parameters:
 *	argv : an array of pointers to command line arguments.
 *	s    : socket handle that operation performed thru it.
 *
 * Returns: int
 *	zero : operation is performed succesfully.
 *	-1   : number of bound interfaces exceed limit.
 *	-2   : the interface not supported.
 *	-3   : another error occurs during operation on NetBEUI.
 */

static int
do_bind (char *argv[], int s)
{
	int   rc;
	struct ifreq   ifr;
	struct netbeui_cfg   rqst;

	memset(&rqst, 0, sizeof(struct netbeui_cfg));
	rqst.command = NB_CFGCMD_NIF_BIND;
	strcpy(rqst.nif_name, argv[2]);

	rc = ioctl(s, NBIOCCONFIG, &rqst);
	if (rc)
		switch (errno) {
			case ENOSPC :
				fprintf(stderr, "%s: Number of bound", argv[0]);
				fprintf(stderr, " interfaces exceed limit.\n");
				return -1;

			case EOPNOTSUPP :
				fprintf(stderr, "%s: The '%s'", argv[0], argv[2]);
				fprintf(stderr, " interface not supported.\n");
				return -2;

			default :
				perror(argv[0]);
				return -3;
		}

	/* We must do the followings, because we can not discover
	   whether NIC is open or not */
	strcpy(ifr.ifr_name, argv[2]);
	rc = ioctl(s, SIOCGIFFLAGS, &ifr);
	if (rc) {
		perror(argv[0]);
		return -4;
	}

	ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
  	rc = ioctl(s, SIOCSIFFLAGS, &ifr);
	if (rc) {
		perror(argv[0]);
		return -5;
	}

	return 0;
}


/*
 * Function: do_unbind
 *	Unbinds NetBEUI protocol stack from a specified network interface.
 *
 * Parameters:
 *	argc : number of passed command line arguments.
 *	argv : an array of pointers to command line arguments.
 *	s    : socket handle that operation performed thru it.
 *
 * Returns: int
 *	zero : operation is performed succesfully.
 *	-4   : the specified switch is invalid.
 *	-5   : an error occurs during operation on NetBEUI.
 */

static int
do_unbind (int argc, char *argv[], int s)
{
	int   rc;
	struct netbeui_cfg   rqst;

	memset(&rqst, 0, sizeof(struct netbeui_cfg));
	rqst.command = NB_CFGCMD_NIF_UNBIND;
	strcpy(rqst.nif_name, argv[2]);
	if (argc > 3)
		if (strcmp(argv[3], "-f") == 0)
			rqst.flag = NB_UNBIND_FLAG_DROP;
		else {
			fprintf(stderr, "%s: Switch '%s' is invalid.\n", argv[0], argv[3]);
			return -4;
		}
	else
		rqst.flag = NB_UNBIND_FLAG_SAFE;

	rc = ioctl(s, NBIOCCONFIG, &rqst);
	if (rc) {
		perror(argv[0]);
		return -5;
	}

#if 0
	{
	struct ifreq   ifr;

	strcpy(ifr.ifr_name, argv[2]);
	rc = ioctl(s, SIOCGIFFLAGS, &ifr);
	if (rc) {
		perror(argv[0]);
		return -6;
	}

	ifr.ifr_flags &= ~IFF_UP;  // IFF_DOWN !!
  	rc = ioctl(s, SIOCSIFFLAGS, &ifr);
	if (rc) {
		perror(argv[0]);
		return -7;
	}
	}
#endif /* 0 */
	return 0;
}


/*
 * Function: do_drop
 *	Drops a NetBIOS session or a LLC link (connection) with all of its
 *	sessions.
 *
 * Parameters:
 *	argc : number of passed command line arguments.
 *	argv : an array of pointers to command line arguments.
 *	s    : socket handle that operation performed thru it.
 *
 * Returns: int
 *	zero : operation is performed succesfully.
 *	-6   : an error occurs during operation.
 */

static int
do_drop (int argc, char *argv[], int s)
{
	int   rc;
	struct netbeui_cfg   rqst;

	memset(&rqst, 0, sizeof(struct netbeui_cfg));
	rqst.ln_num = atoi(argv[2]);
	if (argc > 3) {
		rqst.command = NB_CFGCMD_DROP_SESS;
		rqst.sn_num = atoi(argv[3]);
	}
	else
		rqst.command = NB_CFGCMD_DROP_LINK;

	rc = ioctl(s, NBIOCCONFIG, &rqst);
	if (rc) {
		perror(argv[0]);
		return -6;
	}

	return 0;
}


/*
 * Function: main
 *	Prints usage message and multiplexes user requests to appropriate
 *	routines.
 *
 * Parameters:
 *	argc : number of passed command line arguments.
 *	argv : an array of pointers to command line arguments.
 *
 * Returns: int
 *	zero     : operation is performed succesfully.
 *	-1 .. -6 : an error occurs during operation.
 *	-7       : no arguments is specified.
 *	-8       : creating of NetBEUI socket fails.
 *	-9       : unknown command is specified.
 */

int
main (int argc, char *argv[])
{
	char   errmsg[80];
	int   i,
	      s,
	      rc,
	      len;
	struct netbeui_status   *ns;

	if (argc < 3) {
		fprintf(stderr, "\nProcom NetBEUI administration utility (ver 1.0)\n");
		fprintf(stderr, "\t(C)Copyright Procom Technology Inc. 1997 \n");
		fprintf(stderr, "\nUsage:\n");
		fprintf(stderr, "       %s  bind <NetInterfaceName>\n", argv[0]);
		fprintf(stderr, "       %s  unbind <NetInterfaceName> [-f]\n", argv[0]);
		fprintf(stderr, "       %s  drop <LinkNum> [<SessionNum>]\n\n", argv[0]);
		return -7;
	}

	s = socket(AF_NETBEUI, SOCK_RAW, 0);
	if (s == -1) {
		sprintf(errmsg, "%s (Creating of NetBEUI socket fails)", argv[0]);
		perror(errmsg);
		return -8;
	}

	if (strcmp(argv[1], "bind") == 0)
		return (do_bind(argv, s));
	else
		if (strcmp(argv[1], "unbind") == 0)
			return (do_unbind(argc, argv, s));
		else
			if (strcmp(argv[1], "drop") == 0)
				return (do_drop(argc, argv, s));
			else {
				fprintf(stderr, "%s : Unknown command '%s' \n",
				        argv[0], argv[1]);
				return -9;
			}
}

