/* Mis-connector - user-space router and NATP for misconfigured hosts on LAN.
Parser for configuration file /etc/whack.conf
Copyright (C) 2004,2021,2022 Petr Silhavy <petr.silhavy@yandex.com>
SPDX-License-Identifier: GPL-3.0-or-later
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 3 of the
License, or (at your option) any later version.
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 mis-connector. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pcap.h>
#include <libnet.h>
#include <gio/gio.h>
#include <gio/gnetworking.h>
#include <error.h>
#include <errno.h>
#include <netlink/netlink.h>
#include <netlink/cache.h>
#include <netlink/route/link.h>
#include <netlink/socket.h>
#include <netlink/route/link.h>
#include <netlink/utils.h>
#include <netlink/route/route.h>
#include <netlink/route/nexthop.h>
#include <netlink/route/rtnl.h>
#include <netlink/cli/route.h>
#include <linux/in_route.h>
#include "whackparam.h"
static gboolean
get_bool(char *s, char *line, int line_no)
{
switch(*s)
{
case '0':
case 'n':
case 'N':
case 'f':
case 'F':
return FALSE ;
case '1':
case 'y':
case 'Y':
case 't':
case 'T':
return TRUE ;
default:
error_at_line(EXIT_FAILURE,0, CONF, line_no, "Can't convert value of <%s> to boolean\n, Expexted 0,N(o),n(o),F(alse),f(alse) or 1,Y(es),y(es),T(rue),t(rue)\n\
>>> %s <<<\n", s, line);
}
return FALSE ; // shht dumb compiler
}
char *delimiters = " \t=-" ;
//char *keys[] = { "Fool_dev", "GW_dev", "Fool_range", "Fool_net", "Decent_net",
//"DNS", NULL } ;
char *keys[] = { "In_dev", "GW_dev", "In_nat", "DHCP_net", "DNS",
// 5
"gw_ip", "gw_mac", "router", "nat", /* WAS: "arp_spoof_only", */ NULL } ;
struct whack_param *
read_conf()
{
struct whack_param *wp = g_slice_alloc0(sizeof *wp);
char *buf = NULL ;
size_t size = 0 ;
FILE *FP ;
int n ;
char ipbuf[16] ;
// int dhcp_mask , in_mask ;
GInetAddressMask *iam ;
GError *ge = NULL ;
GInetAddress *addr;
guint len;
GSocketFamily *family ;
const guint8 *bytes;
gboolean any;
gboolean loopback;
gboolean link_local;
gboolean site_local;
gboolean multicast;
int err ;
int line_no = 0 ;
if ( ( FP = fopen(CONF,"r")) != NULL )
config_file = CONF ;
else if ( ( FP = fopen(DEFCONF,"r")) != NULL )
config_file = DEFCONF ;
else
error(1, errno, "Can't open: %s or %s\n", CONF, DEFCONF);
while (1)
{
char *key , *v ;
int i ;
struct in_addr ina ;
if ( (n = getline(&buf, &size, FP )) == EOF )
break ;
++line_no ;
if ( *buf == '#' ) continue ;
if ( (v = strchr(buf, '\n' ) ) != NULL )
*v = 0 ;
key = strtok(buf, delimiters);
for ( i = 0 ; keys[i] ; ++i )
if ( ! strcasecmp(keys[i], key) )
break ;
switch ( i )
{
case 0: /* "Fool_dev" */
wp->in_dev = strdup(strtok(NULL, delimiters));
break ;
case 1: /* "GW_dev" */
wp->gw_dev = strdup(strtok(NULL, delimiters));
break ;
case 2: /* "in_nat" */
v = strdup(strtok(NULL, delimiters));
if ( ( iam = g_inet_address_mask_new_from_string (v, &ge)) == NULL )
error(EXIT_FAILURE,errno, "Malformed in_nat «%s» - %s\n", v, ge->message );
// g_error_free (err);
g_object_get (iam,
"family", &family,
"address", &addr,
"length", &len,
NULL);
g_object_get (addr,
"family", &family,
"bytes", &bytes,
"is-any", &any,
"is-loopback", &loopback,
"is-link-local", &link_local,
"is-site-local", &site_local,
"is-multicast", &multicast, NULL );
wp->in_nat_addr_size = g_inet_address_get_native_size (addr);
switch ( wp->in_nat_addr_size ) {
case SIZEOF_IN_ADDR:
case SIZEOF_IN6_ADDR: break ;
default: error(EXIT_FAILURE,errno, "Unknow address size ˝%d˝ \n", wp->in_nat_addr_size ); break ;
}
wp->nl_nat_addr = nl_addr_alloc( wp->in_nat_addr_size );
if ( ( err = nl_addr_parse ( v, AF_UNSPEC, &wp->nl_nat_addr ) ) != 0 )
error(EXIT_FAILURE,errno, "nl_addr_parse parse error : ˝%s˝\n",
nl_geterror(err) );
{
const guint8 *bt = g_inet_address_to_bytes(addr) ;
memcpy(&wp->in_min, bt, sizeof (int) );
wp->in_nat = wp->in_min ; // skip net number
}
// mask
{
int n = ( __CHAR_BIT__ * wp->in_nat_addr_size ) - len ;
wp->in_len = len ;
wp->in_size = 1 << n ;
// wp->in_mask = ntohl(0xffffffff << ( n )) ;
// int n2 = wp->in_size - 1; //
wp->in_max = wp->in_min + wp->in_size - 1 ; // | ~wp->in_mask ;
}
break ;
case 3: /* DHCP net */
v = strdup(strtok(NULL, delimiters));
if ( ( iam = g_inet_address_mask_new_from_string (v, &ge)) == NULL )
error(EXIT_FAILURE,errno, "Malformed %s «%s» - %s\n", key, v, ge->message );
// g_error_free (err);
g_object_get (iam,
"family", &family,
"address", &addr,
"length", &len,
NULL);
g_object_get (addr,
"family", &family,
"bytes", &bytes,
"is-any", &any,
"is-loopback", &loopback,
"is-link-local", &link_local,
"is-site-local", &site_local,
"is-multicast", &multicast, NULL );
{
const guint8 *bt = g_inet_address_to_bytes(addr) ;
memcpy(&wp->dhcp_net, bt, sizeof (int) );
}
// mask
{
int n = 32 - len ;
wp->dhcp_len = len ;
wp->dhcp_mask = ntohl(0xffffffff << ( n )) ;
}
break ;
case 4: /* DNS */
v = strdup(strtok(NULL, delimiters));
if ( !inet_aton (v, &ina))
BUG("Malformed IP address `%s'\n",v);
wp->dns = ina.s_addr ;
break ;
case 5:/* default gw */
v = strdup(strtok(NULL, delimiters));
wp->gw_ip = g_inet_address_new_from_string (v);
wp->gw_ip_size = g_inet_address_get_native_size (wp->gw_ip);
wp->gw_ip_bytes = g_slice_alloc0( wp->gw_ip_size );
memcpy( wp->gw_ip_bytes, g_inet_address_to_bytes(wp->gw_ip), wp->gw_ip_size);
break ;
case 6: /* default gw's MAC */
v = strdup(strtok(NULL, delimiters));
char **stra = g_strsplit(v, ":", 6);
for ( int i = 0 ; stra[i] && i < ETH_ALEN ; ++i )
{
wp->gw_mac[i] = strtoul( stra[i], NULL, 16 );
}
g_strfreev(stra);
break ;
case 7: // router
v = /* g_strdup( */strtok(NULL, delimiters);
wp->router = get_bool(v, buf, line_no);
// g_free(v);
break ;
case 8: // NAT vs arp_spoof_only
v = /* g_strdup( */strtok(NULL, delimiters);
wp->nat = get_bool(v, buf, line_no);
break ;
default:
BUG("Unknow configuration option `%s'\n", key );
break ;
}
}
printf("%s %s\n", PACKAGE, VERSION);
printf("%s Libnet %s\n", pcap_lib_version() , LIBNET_VERSION);
printf("Internal interface : %s\n", wp->in_dev);
printf("Interface to gateway : %s\n", wp->gw_dev);
inet_ntop(AF_INET, &wp->in_min, ipbuf, sizeof ipbuf);
printf("IP range : %s-", ipbuf);
inet_ntop(AF_INET, &wp->in_max, ipbuf, sizeof ipbuf);
printf("%s\n",ipbuf);
inet_ntop(AF_INET, &wp->in_nat, ipbuf, sizeof ipbuf);
printf("Internal net : %s\n", ipbuf);
asprintf( &wp->gw_filter_app, "dst net %s/%d", ipbuf, wp->in_len );
inet_ntop(AF_INET, &wp->in_mask, ipbuf, sizeof ipbuf);
printf("Internal netmask : %s\n", ipbuf);
wp->in_size = htonl(wp->in_max) - htonl(wp->in_min) + 1 ;
printf("Free IP addresses : %u\n", wp->in_size);
inet_ntop(AF_INET, &wp->dhcp_net, ipbuf, sizeof ipbuf);
printf("DHCP's net : %s\n", ipbuf);
/* not dst OKNET ether dst */
asprintf( &wp->filter_app, "not src net %s/%d", ipbuf, wp->dhcp_len);
printf("Internal filter : %s\n", wp->filter_app);
printf("Gateway filter : %s\n", wp->gw_filter_app);
inet_ntop(AF_INET, &wp->dns, ipbuf, sizeof ipbuf);
printf("DNS : %s\n", ipbuf);
char *tmp_ip = g_inet_address_to_string(wp->gw_ip);
printf("Default gateway : %s\n", tmp_ip );
g_free(tmp_ip);
tmp_ip = mac2str( wp->gw_mac );
printf("Default gateway's MAC : %s\n", tmp_ip );
g_free(tmp_ip);
printf("Router : %s\n", wp->router ? "Yes":"No" );
printf("NAT : %s\n", wp->nat ? "Yes":"No" );
/* char filter_app[] = "not src net 192.168.100.0/23" */
/* "ether proto arp" */; /* The filter expression */
/* char gw_filter_app[] = "dst net 192.168.101.0/24" */
free(buf);
return wp ;
}