blob: cc0eb183bde34c14a227ae1f70246e058c1032fa [file] [log] [blame]
%{
/*
* (C) 2006-2009 by Pablo Neira Ayuso <pablo@netfilter.org>
*
* 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 2 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Description: configuration file abstract grammar
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>
#include <stdarg.h>
#include "conntrackd.h"
#include "bitops.h"
#include "cidr.h"
#include "helper.h"
#include "stack.h"
#include <syslog.h>
#include <sched.h>
#include <dlfcn.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack_tcp.h>
extern char *yytext;
extern int yylineno;
struct ct_conf conf;
enum {
CTD_CFG_ERROR = 0,
CTD_CFG_WARN,
};
static void print_err(int err, const char *msg, ...);
static void __kernel_filter_start(void);
static void __kernel_filter_add_state(int value);
static void __max_dedicated_links_reached(void);
struct stack symbol_stack;
enum {
SYMBOL_HELPER_QUEUE_NUM,
SYMBOL_HELPER_QUEUE_LEN,
SYMBOL_HELPER_POLICY_EXPECT_ROOT,
SYMBOL_HELPER_EXPECT_POLICY_LEAF,
};
%}
%union {
int val;
char *string;
}
%token T_IPV4_ADDR T_IPV4_IFACE T_PORT T_HASHSIZE T_HASHLIMIT T_MULTICAST
%token T_PATH T_UNIX T_REFRESH T_IPV6_ADDR T_IPV6_IFACE
%token T_IGNORE_UDP T_IGNORE_ICMP T_IGNORE_TRAFFIC T_BACKLOG T_GROUP
%token T_LOG T_UDP T_ICMP T_IGMP T_VRRP T_TCP T_IGNORE_PROTOCOL
%token T_LOCK T_STRIP_NAT T_BUFFER_SIZE_MAX_GROWN T_EXPIRE T_TIMEOUT
%token T_GENERAL T_SYNC T_STATS T_RELAX_TRANSITIONS T_BUFFER_SIZE T_DELAY
%token T_SYNC_MODE T_LISTEN_TO T_FAMILY T_RESEND_BUFFER_SIZE
%token T_ALARM T_FTFW T_CHECKSUM T_WINDOWSIZE T_ON T_OFF
%token T_REPLICATE T_FOR T_IFACE T_PURGE T_RESEND_QUEUE_SIZE
%token T_ESTABLISHED T_SYN_SENT T_SYN_RECV T_FIN_WAIT
%token T_CLOSE_WAIT T_LAST_ACK T_TIME_WAIT T_CLOSE T_LISTEN
%token T_SYSLOG T_WRITE_THROUGH T_STAT_BUFFER_SIZE T_DESTROY_TIMEOUT
%token T_RCVBUFF T_SNDBUFF T_NOTRACK T_POLL_SECS
%token T_FILTER T_ADDRESS T_PROTOCOL T_STATE T_ACCEPT T_IGNORE
%token T_FROM T_USERSPACE T_KERNELSPACE T_EVENT_ITER_LIMIT T_DEFAULT
%token T_NETLINK_OVERRUN_RESYNC T_NICE T_IPV4_DEST_ADDR T_IPV6_DEST_ADDR
%token T_SCHEDULER T_TYPE T_PRIO T_NETLINK_EVENTS_RELIABLE
%token T_DISABLE_INTERNAL_CACHE T_DISABLE_EXTERNAL_CACHE T_ERROR_QUEUE_LENGTH
%token T_OPTIONS T_TCP_WINDOW_TRACKING T_EXPECT_SYNC
%token T_HELPER T_HELPER_QUEUE_NUM T_HELPER_QUEUE_LEN T_HELPER_POLICY
%token T_HELPER_EXPECT_TIMEOUT T_HELPER_EXPECT_MAX
%token T_SYSTEMD
%token <string> T_IP T_PATH_VAL
%token <val> T_NUMBER
%token <val> T_SIGNED_NUMBER
%token <string> T_STRING
%%
configfile :
| lines
;
lines : line
| lines line
;
line : ignore_protocol
| ignore_traffic
| strip_nat
| general
| sync
| stats
| helper
;
logfile_bool : T_LOG T_ON
{
strncpy(conf.logfile, DEFAULT_LOGFILE, FILENAME_MAXLEN);
};
logfile_bool : T_LOG T_OFF
{
};
logfile_path : T_LOG T_PATH_VAL
{
strncpy(conf.logfile, $2, FILENAME_MAXLEN);
};
syslog_bool : T_SYSLOG T_ON
{
conf.syslog_facility = DEFAULT_SYSLOG_FACILITY;
};
syslog_bool : T_SYSLOG T_OFF
{
conf.syslog_facility = -1;
}
syslog_facility : T_SYSLOG T_STRING
{
if (!strcmp($2, "daemon"))
conf.syslog_facility = LOG_DAEMON;
else if (!strcmp($2, "local0"))
conf.syslog_facility = LOG_LOCAL0;
else if (!strcmp($2, "local1"))
conf.syslog_facility = LOG_LOCAL1;
else if (!strcmp($2, "local2"))
conf.syslog_facility = LOG_LOCAL2;
else if (!strcmp($2, "local3"))
conf.syslog_facility = LOG_LOCAL3;
else if (!strcmp($2, "local4"))
conf.syslog_facility = LOG_LOCAL4;
else if (!strcmp($2, "local5"))
conf.syslog_facility = LOG_LOCAL5;
else if (!strcmp($2, "local6"))
conf.syslog_facility = LOG_LOCAL6;
else if (!strcmp($2, "local7"))
conf.syslog_facility = LOG_LOCAL7;
else {
print_err(CTD_CFG_WARN, "'%s' is not a known syslog facility, "
"ignoring", $2);
break;
}
if (conf.stats.syslog_facility != -1 &&
conf.syslog_facility != conf.stats.syslog_facility)
print_err(CTD_CFG_WARN, "conflicting Syslog facility "
"values, defaulting to General");
};
lock : T_LOCK T_PATH_VAL
{
strncpy(conf.lockfile, $2, FILENAME_MAXLEN);
};
strip_nat: T_STRIP_NAT
{
print_err(CTD_CFG_WARN, "`StripNAT' clause is obsolete, ignoring");
};
refreshtime : T_REFRESH T_NUMBER
{
conf.refresh = $2;
};
expiretime: T_EXPIRE T_NUMBER
{
conf.cache_timeout = $2;
};
timeout: T_TIMEOUT T_NUMBER
{
conf.commit_timeout = $2;
};
purge: T_PURGE T_NUMBER
{
conf.purge_timeout = $2;
};
checksum: T_CHECKSUM T_ON
{
print_err(CTD_CFG_WARN, "the use of `Checksum' outside the "
"`Multicast' clause is ambiguous");
/*
* XXX: The use of Checksum outside of the Multicast clause is broken
* if we have more than one dedicated links.
*/
conf.channel[0].u.mcast.checksum = 0;
};
checksum: T_CHECKSUM T_OFF
{
print_err(CTD_CFG_WARN, "the use of `Checksum' outside the "
"`Multicast' clause is ambiguous");
/*
* XXX: The use of Checksum outside of the Multicast clause is broken
* if we have more than one dedicated links.
*/
conf.channel[0].u.mcast.checksum = 1;
};
ignore_traffic : T_IGNORE_TRAFFIC '{' ignore_traffic_options '}'
{
ct_filter_set_logic(STATE(us_filter),
CT_FILTER_ADDRESS,
CT_FILTER_NEGATIVE);
print_err(CTD_CFG_WARN, "the clause `IgnoreTrafficFor' is obsolete. "
"Use `Filter' instead");
};
ignore_traffic_options :
| ignore_traffic_options ignore_traffic_option;
ignore_traffic_option : T_IPV4_ADDR T_IP
{
union inet_address ip;
memset(&ip, 0, sizeof(union inet_address));
if (!inet_aton($2, &ip.ipv4)) {
print_err(CTD_CFG_WARN, "%s is not a valid IPv4, "
"ignoring", $2);
break;
}
if (!ct_filter_add_ip(STATE(us_filter), &ip, AF_INET)) {
if (errno == EEXIST)
print_err(CTD_CFG_WARN, "IP %s is repeated "
"in the ignore pool", $2);
if (errno == ENOSPC)
print_err(CTD_CFG_WARN, "too many IP in the "
"ignore pool!");
}
};
ignore_traffic_option : T_IPV6_ADDR T_IP
{
union inet_address ip;
memset(&ip, 0, sizeof(union inet_address));
#ifdef HAVE_INET_PTON_IPV6
if (inet_pton(AF_INET6, $2, &ip.ipv6) <= 0) {
print_err(CTD_CFG_WARN, "%s is not a valid IPv6, ignoring", $2);
break;
}
#else
print_err(CTD_CFG_WARN, "cannot find inet_pton(), IPv6 unsupported!");
#endif
if (!ct_filter_add_ip(STATE(us_filter), &ip, AF_INET6)) {
if (errno == EEXIST)
print_err(CTD_CFG_WARN, "IP %s is repeated "
"in the ignore pool", $2);
if (errno == ENOSPC)
print_err(CTD_CFG_WARN, "too many IP in the "
"ignore pool!");
}
};
multicast_line : T_MULTICAST '{' multicast_options '}'
{
if (conf.channel_type_global != CHANNEL_NONE &&
conf.channel_type_global != CHANNEL_MCAST) {
print_err(CTD_CFG_ERROR, "cannot use `Multicast' with other "
"dedicated link protocols!");
exit(EXIT_FAILURE);
}
conf.channel_type_global = CHANNEL_MCAST;
conf.channel[conf.channel_num].channel_type = CHANNEL_MCAST;
conf.channel[conf.channel_num].channel_flags = CHANNEL_F_BUFFERED;
conf.channel_num++;
};
multicast_line : T_MULTICAST T_DEFAULT '{' multicast_options '}'
{
if (conf.channel_type_global != CHANNEL_NONE &&
conf.channel_type_global != CHANNEL_MCAST) {
print_err(CTD_CFG_ERROR, "cannot use `Multicast' with other "
"dedicated link protocols!");
exit(EXIT_FAILURE);
}
conf.channel_type_global = CHANNEL_MCAST;
conf.channel[conf.channel_num].channel_type = CHANNEL_MCAST;
conf.channel[conf.channel_num].channel_flags = CHANNEL_F_DEFAULT |
CHANNEL_F_BUFFERED;
conf.channel_default = conf.channel_num;
conf.channel_num++;
};
multicast_options :
| multicast_options multicast_option;
multicast_option : T_IPV4_ADDR T_IP
{
__max_dedicated_links_reached();
if (!inet_aton($2, &conf.channel[conf.channel_num].u.mcast.in)) {
print_err(CTD_CFG_WARN, "%s is not a valid IPv4 address", $2);
break;
}
if (conf.channel[conf.channel_num].u.mcast.ipproto == AF_INET6) {
print_err(CTD_CFG_WARN, "your multicast address is IPv4 but "
"is binded to an IPv6 interface? "
"Surely, this is not what you want");
break;
}
conf.channel[conf.channel_num].u.mcast.ipproto = AF_INET;
};
multicast_option : T_IPV6_ADDR T_IP
{
__max_dedicated_links_reached();
#ifdef HAVE_INET_PTON_IPV6
if (inet_pton(AF_INET6, $2,
&conf.channel[conf.channel_num].u.mcast.in) <= 0) {
print_err(CTD_CFG_WARN, "%s is not a valid IPv6 address", $2);
break;
}
#else
print_err(CTD_CFG_WARN, "cannot find inet_pton(), IPv6 unsupported!");
break;
#endif
if (conf.channel[conf.channel_num].u.mcast.ipproto == AF_INET) {
print_err(CTD_CFG_WARN, "your multicast address is IPv6 but "
"is binded to an IPv4 interface? "
"Surely this is not what you want");
break;
}
conf.channel[conf.channel_num].u.mcast.ipproto = AF_INET6;
if (conf.channel[conf.channel_num].channel_ifname[0] &&
!conf.channel[conf.channel_num].u.mcast.ifa.interface_index6) {
unsigned int idx;
idx = if_nametoindex($2);
if (!idx) {
print_err(CTD_CFG_WARN,
"%s is an invalid interface", $2);
break;
}
conf.channel[conf.channel_num].u.mcast.ifa.interface_index6 = idx;
conf.channel[conf.channel_num].u.mcast.ipproto = AF_INET6;
}
};
multicast_option : T_IPV4_IFACE T_IP
{
__max_dedicated_links_reached();
if (!inet_aton($2, &conf.channel[conf.channel_num].u.mcast.ifa)) {
print_err(CTD_CFG_WARN, "%s is not a valid IPv4 address", $2);
break;
}
if (conf.channel[conf.channel_num].u.mcast.ipproto == AF_INET6) {
print_err(CTD_CFG_WARN, "your multicast interface is IPv4 but "
"is binded to an IPv6 interface? "
"Surely, this is not what you want");
break;
}
conf.channel[conf.channel_num].u.mcast.ipproto = AF_INET;
};
multicast_option : T_IPV6_IFACE T_IP
{
print_err(CTD_CFG_WARN, "`IPv6_interface' not required, ignoring");
}
multicast_option : T_IFACE T_STRING
{
unsigned int idx;
__max_dedicated_links_reached();
strncpy(conf.channel[conf.channel_num].channel_ifname, $2, IFNAMSIZ);
idx = if_nametoindex($2);
if (!idx) {
print_err(CTD_CFG_WARN, "%s is an invalid interface", $2);
break;
}
if (conf.channel[conf.channel_num].u.mcast.ipproto == AF_INET6) {
conf.channel[conf.channel_num].u.mcast.ifa.interface_index6 = idx;
conf.channel[conf.channel_num].u.mcast.ipproto = AF_INET6;
}
};
multicast_option : T_BACKLOG T_NUMBER
{
print_err(CTD_CFG_WARN, "`Backlog' option inside Multicast clause is "
"obsolete. Please, remove it from "
"conntrackd.conf");
};
multicast_option : T_GROUP T_NUMBER
{
__max_dedicated_links_reached();
conf.channel[conf.channel_num].u.mcast.port = $2;
};
multicast_option: T_SNDBUFF T_NUMBER
{
__max_dedicated_links_reached();
conf.channel[conf.channel_num].u.mcast.sndbuf = $2;
};
multicast_option: T_RCVBUFF T_NUMBER
{
__max_dedicated_links_reached();
conf.channel[conf.channel_num].u.mcast.rcvbuf = $2;
};
multicast_option: T_CHECKSUM T_ON
{
__max_dedicated_links_reached();
conf.channel[conf.channel_num].u.mcast.checksum = 0;
};
multicast_option: T_CHECKSUM T_OFF
{
__max_dedicated_links_reached();
conf.channel[conf.channel_num].u.mcast.checksum = 1;
};
udp_line : T_UDP '{' udp_options '}'
{
if (conf.channel_type_global != CHANNEL_NONE &&
conf.channel_type_global != CHANNEL_UDP) {
print_err(CTD_CFG_ERROR, "cannot use `UDP' with other "
"dedicated link protocols!");
exit(EXIT_FAILURE);
}
conf.channel_type_global = CHANNEL_UDP;
conf.channel[conf.channel_num].channel_type = CHANNEL_UDP;
conf.channel[conf.channel_num].channel_flags = CHANNEL_F_BUFFERED;
conf.channel_num++;
};
udp_line : T_UDP T_DEFAULT '{' udp_options '}'
{
if (conf.channel_type_global != CHANNEL_NONE &&
conf.channel_type_global != CHANNEL_UDP) {
print_err(CTD_CFG_ERROR, "cannot use `UDP' with other "
"dedicated link protocols!");
exit(EXIT_FAILURE);
}
conf.channel_type_global = CHANNEL_UDP;
conf.channel[conf.channel_num].channel_type = CHANNEL_UDP;
conf.channel[conf.channel_num].channel_flags = CHANNEL_F_DEFAULT |
CHANNEL_F_BUFFERED;
conf.channel_default = conf.channel_num;
conf.channel_num++;
};
udp_options :
| udp_options udp_option;
udp_option : T_IPV4_ADDR T_IP
{
__max_dedicated_links_reached();
if (!inet_aton($2, &conf.channel[conf.channel_num].u.udp.server.ipv4)) {
print_err(CTD_CFG_WARN, "%s is not a valid IPv4 address", $2);
break;
}
conf.channel[conf.channel_num].u.udp.ipproto = AF_INET;
};
udp_option : T_IPV6_ADDR T_IP
{
__max_dedicated_links_reached();
#ifdef HAVE_INET_PTON_IPV6
if (inet_pton(AF_INET6, $2,
&conf.channel[conf.channel_num].u.udp.server.ipv6) <= 0) {
print_err(CTD_CFG_WARN, "%s is not a valid IPv6 address", $2);
break;
}
#else
print_err(CTD_CFG_WARN, "cannot find inet_pton(), IPv6 unsupported!");
break;
#endif
conf.channel[conf.channel_num].u.udp.ipproto = AF_INET6;
};
udp_option : T_IPV4_DEST_ADDR T_IP
{
__max_dedicated_links_reached();
if (!inet_aton($2, &conf.channel[conf.channel_num].u.udp.client)) {
print_err(CTD_CFG_WARN, "%s is not a valid IPv4 address", $2);
break;
}
conf.channel[conf.channel_num].u.udp.ipproto = AF_INET;
};
udp_option : T_IPV6_DEST_ADDR T_IP
{
__max_dedicated_links_reached();
#ifdef HAVE_INET_PTON_IPV6
if (inet_pton(AF_INET6, $2,
&conf.channel[conf.channel_num].u.udp.client) <= 0) {
print_err(CTD_CFG_WARN, "%s is not a valid IPv6 address", $2);
break;
}
#else
print_err(CTD_CFG_WARN, "cannot find inet_pton(), IPv6 unsupported!");
break;
#endif
conf.channel[conf.channel_num].u.udp.ipproto = AF_INET6;
};
udp_option : T_IFACE T_STRING
{
int idx;
__max_dedicated_links_reached();
strncpy(conf.channel[conf.channel_num].channel_ifname, $2, IFNAMSIZ);
idx = if_nametoindex($2);
if (!idx) {
print_err(CTD_CFG_WARN, "%s is an invalid interface", $2);
break;
}
conf.channel[conf.channel_num].u.udp.server.ipv6.scope_id = idx;
};
udp_option : T_PORT T_NUMBER
{
__max_dedicated_links_reached();
conf.channel[conf.channel_num].u.udp.port = $2;
};
udp_option: T_SNDBUFF T_NUMBER
{
__max_dedicated_links_reached();
conf.channel[conf.channel_num].u.udp.sndbuf = $2;
};
udp_option: T_RCVBUFF T_NUMBER
{
__max_dedicated_links_reached();
conf.channel[conf.channel_num].u.udp.rcvbuf = $2;
};
udp_option: T_CHECKSUM T_ON
{
__max_dedicated_links_reached();
conf.channel[conf.channel_num].u.udp.checksum = 0;
};
udp_option: T_CHECKSUM T_OFF
{
__max_dedicated_links_reached();
conf.channel[conf.channel_num].u.udp.checksum = 1;
};
tcp_line : T_TCP '{' tcp_options '}'
{
if (conf.channel_type_global != CHANNEL_NONE &&
conf.channel_type_global != CHANNEL_TCP) {
print_err(CTD_CFG_ERROR, "cannot use `TCP' with other "
"dedicated link protocols!");
exit(EXIT_FAILURE);
}
conf.channel_type_global = CHANNEL_TCP;
conf.channel[conf.channel_num].channel_type = CHANNEL_TCP;
conf.channel[conf.channel_num].channel_flags = CHANNEL_F_BUFFERED |
CHANNEL_F_STREAM |
CHANNEL_F_ERRORS;
conf.channel_num++;
};
tcp_line : T_TCP T_DEFAULT '{' tcp_options '}'
{
if (conf.channel_type_global != CHANNEL_NONE &&
conf.channel_type_global != CHANNEL_TCP) {
print_err(CTD_CFG_ERROR, "cannot use `TCP' with other "
"dedicated link protocols!");
exit(EXIT_FAILURE);
}
conf.channel_type_global = CHANNEL_TCP;
conf.channel[conf.channel_num].channel_type = CHANNEL_TCP;
conf.channel[conf.channel_num].channel_flags = CHANNEL_F_DEFAULT |
CHANNEL_F_BUFFERED |
CHANNEL_F_STREAM |
CHANNEL_F_ERRORS;
conf.channel_default = conf.channel_num;
conf.channel_num++;
};
tcp_options :
| tcp_options tcp_option;
tcp_option : T_IPV4_ADDR T_IP
{
__max_dedicated_links_reached();
if (!inet_aton($2, &conf.channel[conf.channel_num].u.tcp.server.ipv4)) {
print_err(CTD_CFG_WARN, "%s is not a valid IPv4 address", $2);
break;
}
conf.channel[conf.channel_num].u.tcp.ipproto = AF_INET;
};
tcp_option : T_IPV6_ADDR T_IP
{
__max_dedicated_links_reached();
#ifdef HAVE_INET_PTON_IPV6
if (inet_pton(AF_INET6, $2,
&conf.channel[conf.channel_num].u.tcp.server.ipv6) <= 0) {
print_err(CTD_CFG_WARN, "%s is not a valid IPv6 address", $2);
break;
}
#else
print_err(CTD_CFG_WARN, "cannot find inet_pton(), IPv6 unsupported!");
break;
#endif
conf.channel[conf.channel_num].u.tcp.ipproto = AF_INET6;
};
tcp_option : T_IPV4_DEST_ADDR T_IP
{
__max_dedicated_links_reached();
if (!inet_aton($2, &conf.channel[conf.channel_num].u.tcp.client)) {
print_err(CTD_CFG_WARN, "%s is not a valid IPv4 address", $2);
break;
}
conf.channel[conf.channel_num].u.tcp.ipproto = AF_INET;
};
tcp_option : T_IPV6_DEST_ADDR T_IP
{
__max_dedicated_links_reached();
#ifdef HAVE_INET_PTON_IPV6
if (inet_pton(AF_INET6, $2,
&conf.channel[conf.channel_num].u.tcp.client) <= 0) {
print_err(CTD_CFG_WARN, "%s is not a valid IPv6 address", $2);
break;
}
#else
print_err(CTD_CFG_WARN, "cannot find inet_pton(), IPv6 unsupported!");
break;
#endif
conf.channel[conf.channel_num].u.tcp.ipproto = AF_INET6;
};
tcp_option : T_IFACE T_STRING
{
int idx;
__max_dedicated_links_reached();
strncpy(conf.channel[conf.channel_num].channel_ifname, $2, IFNAMSIZ);
idx = if_nametoindex($2);
if (!idx) {
print_err(CTD_CFG_WARN, "%s is an invalid interface", $2);
break;
}
conf.channel[conf.channel_num].u.tcp.server.ipv6.scope_id = idx;
};
tcp_option : T_PORT T_NUMBER
{
__max_dedicated_links_reached();
conf.channel[conf.channel_num].u.tcp.port = $2;
};
tcp_option: T_SNDBUFF T_NUMBER
{
__max_dedicated_links_reached();
conf.channel[conf.channel_num].u.tcp.sndbuf = $2;
};
tcp_option: T_RCVBUFF T_NUMBER
{
__max_dedicated_links_reached();
conf.channel[conf.channel_num].u.tcp.rcvbuf = $2;
};
tcp_option: T_CHECKSUM T_ON
{
__max_dedicated_links_reached();
conf.channel[conf.channel_num].u.tcp.checksum = 0;
};
tcp_option: T_CHECKSUM T_OFF
{
__max_dedicated_links_reached();
conf.channel[conf.channel_num].u.tcp.checksum = 1;
};
tcp_option: T_ERROR_QUEUE_LENGTH T_NUMBER
{
__max_dedicated_links_reached();
CONFIG(channelc).error_queue_length = $2;
};
hashsize : T_HASHSIZE T_NUMBER
{
conf.hashsize = $2;
};
hashlimit: T_HASHLIMIT T_NUMBER
{
conf.limit = $2;
};
unix_line: T_UNIX '{' unix_options '}';
unix_options:
| unix_options unix_option
;
unix_option : T_PATH T_PATH_VAL
{
strcpy(conf.local.path, $2);
};
unix_option : T_BACKLOG T_NUMBER
{
conf.local.backlog = $2;
};
ignore_protocol: T_IGNORE_PROTOCOL '{' ignore_proto_list '}'
{
ct_filter_set_logic(STATE(us_filter),
CT_FILTER_L4PROTO,
CT_FILTER_NEGATIVE);
print_err(CTD_CFG_WARN, "the clause `IgnoreProtocol' is "
"obsolete. Use `Filter' instead");
};
ignore_proto_list:
| ignore_proto_list ignore_proto
;
ignore_proto: T_NUMBER
{
if ($1 < IPPROTO_MAX)
ct_filter_add_proto(STATE(us_filter), $1);
else
print_err(CTD_CFG_WARN, "protocol number `%d' is freak", $1);
};
ignore_proto: T_STRING
{
struct protoent *pent;
pent = getprotobyname($1);
if (pent == NULL) {
print_err(CTD_CFG_WARN, "getprotobyname() cannot find "
"protocol `%s' in /etc/protocols", $1);
break;
}
ct_filter_add_proto(STATE(us_filter), pent->p_proto);
};
sync: T_SYNC '{' sync_list '}'
{
if (conf.flags & CTD_STATS_MODE) {
print_err(CTD_CFG_ERROR, "cannot use both `Stats' and `Sync' "
"clauses in conntrackd.conf");
exit(EXIT_FAILURE);
}
conf.flags |= CTD_SYNC_MODE;
};
sync_list:
| sync_list sync_line;
sync_line: refreshtime
| expiretime
| timeout
| purge
| checksum
| multicast_line
| udp_line
| tcp_line
| relax_transitions
| delay_destroy_msgs
| sync_mode_alarm
| sync_mode_ftfw
| sync_mode_notrack
| listen_to
| state_replication
| cache_writethrough
| destroy_timeout
| option_line
;
option_line: T_OPTIONS '{' options '}';
options:
| options option
;
option: T_TCP_WINDOW_TRACKING T_ON
{
CONFIG(sync).tcp_window_tracking = 1;
};
option: T_TCP_WINDOW_TRACKING T_OFF
{
CONFIG(sync).tcp_window_tracking = 0;
};
option: T_EXPECT_SYNC T_ON
{
CONFIG(flags) |= CTD_EXPECT;
CONFIG(netlink).subsys_id = NFNL_SUBSYS_NONE;
CONFIG(netlink).groups = NF_NETLINK_CONNTRACK_NEW |
NF_NETLINK_CONNTRACK_UPDATE |
NF_NETLINK_CONNTRACK_DESTROY |
NF_NETLINK_CONNTRACK_EXP_NEW |
NF_NETLINK_CONNTRACK_EXP_UPDATE |
NF_NETLINK_CONNTRACK_EXP_DESTROY;
};
option: T_EXPECT_SYNC T_OFF
{
CONFIG(netlink).subsys_id = NFNL_SUBSYS_CTNETLINK;
CONFIG(netlink).groups = NF_NETLINK_CONNTRACK_NEW |
NF_NETLINK_CONNTRACK_UPDATE |
NF_NETLINK_CONNTRACK_DESTROY;
};
option: T_EXPECT_SYNC '{' expect_list '}'
{
CONFIG(flags) |= CTD_EXPECT;
CONFIG(netlink).subsys_id = NFNL_SUBSYS_NONE;
CONFIG(netlink).groups = NF_NETLINK_CONNTRACK_NEW |
NF_NETLINK_CONNTRACK_UPDATE |
NF_NETLINK_CONNTRACK_DESTROY |
NF_NETLINK_CONNTRACK_EXP_NEW |
NF_NETLINK_CONNTRACK_EXP_UPDATE |
NF_NETLINK_CONNTRACK_EXP_DESTROY;
};
expect_list:
| expect_list expect_item ;
expect_item: T_STRING
{
exp_filter_add(STATE(exp_filter), $1);
}
sync_mode_alarm: T_SYNC_MODE T_ALARM '{' sync_mode_alarm_list '}'
{
conf.flags |= CTD_SYNC_ALARM;
};
sync_mode_ftfw: T_SYNC_MODE T_FTFW '{' sync_mode_ftfw_list '}'
{
conf.flags |= CTD_SYNC_FTFW;
};
sync_mode_notrack: T_SYNC_MODE T_NOTRACK '{' sync_mode_notrack_list '}'
{
conf.flags |= CTD_SYNC_NOTRACK;
};
sync_mode_alarm_list:
| sync_mode_alarm_list sync_mode_alarm_line;
sync_mode_alarm_line: refreshtime
| expiretime
| timeout
| purge
| relax_transitions
| delay_destroy_msgs
;
sync_mode_ftfw_list:
| sync_mode_ftfw_list sync_mode_ftfw_line;
sync_mode_ftfw_line: resend_queue_size
| resend_buffer_size
| timeout
| purge
| window_size
| disable_external_cache
;
sync_mode_notrack_list:
| sync_mode_notrack_list sync_mode_notrack_line;
sync_mode_notrack_line: timeout
| purge
| disable_internal_cache
| disable_external_cache
;
disable_internal_cache: T_DISABLE_INTERNAL_CACHE T_ON
{
conf.sync.internal_cache_disable = 1;
};
disable_internal_cache: T_DISABLE_INTERNAL_CACHE T_OFF
{
conf.sync.internal_cache_disable = 0;
};
disable_external_cache: T_DISABLE_EXTERNAL_CACHE T_ON
{
conf.sync.external_cache_disable = 1;
};
disable_external_cache: T_DISABLE_EXTERNAL_CACHE T_OFF
{
conf.sync.external_cache_disable = 0;
};
resend_buffer_size: T_RESEND_BUFFER_SIZE T_NUMBER
{
print_err(CTD_CFG_WARN, "`ResendBufferSize' is deprecated. "
"Use `ResendQueueSize' instead");
};
resend_queue_size: T_RESEND_QUEUE_SIZE T_NUMBER
{
conf.resend_queue_size = $2;
};
window_size: T_WINDOWSIZE T_NUMBER
{
conf.window_size = $2;
};
destroy_timeout: T_DESTROY_TIMEOUT T_NUMBER
{
print_err(CTD_CFG_WARN, "`DestroyTimeout' is deprecated. Remove it");
};
relax_transitions: T_RELAX_TRANSITIONS
{
print_err(CTD_CFG_WARN, "`RelaxTransitions' clause is obsolete. "
"Please, remove it from conntrackd.conf");
};
delay_destroy_msgs: T_DELAY
{
print_err(CTD_CFG_WARN, "`DelayDestroyMessages' clause is obsolete. "
"Please, remove it from conntrackd.conf");
};
listen_to: T_LISTEN_TO T_IP
{
print_err(CTD_CFG_WARN, "the clause `ListenTo' is obsolete, ignoring");
};
state_replication: T_REPLICATE states T_FOR state_proto
{
ct_filter_set_logic(STATE(us_filter),
CT_FILTER_STATE,
CT_FILTER_POSITIVE);
print_err(CTD_CFG_WARN, "the clause `Replicate' is obsolete. "
"Use `Filter' instead");
};
states:
| states state;
state_proto: T_STRING
{
if (strncmp($1, "TCP", strlen("TCP")) != 0) {
print_err(CTD_CFG_WARN, "unsupported protocol `%s' in line %d",
$1, yylineno);
}
};
state: tcp_state;
tcp_states:
| tcp_states tcp_state;
tcp_state: T_SYN_SENT
{
ct_filter_add_state(STATE(us_filter),
IPPROTO_TCP,
TCP_CONNTRACK_SYN_SENT);
__kernel_filter_add_state(TCP_CONNTRACK_SYN_SENT);
};
tcp_state: T_SYN_RECV
{
ct_filter_add_state(STATE(us_filter),
IPPROTO_TCP,
TCP_CONNTRACK_SYN_RECV);
__kernel_filter_add_state(TCP_CONNTRACK_SYN_RECV);
};
tcp_state: T_ESTABLISHED
{
ct_filter_add_state(STATE(us_filter),
IPPROTO_TCP,
TCP_CONNTRACK_ESTABLISHED);
__kernel_filter_add_state(TCP_CONNTRACK_ESTABLISHED);
};
tcp_state: T_FIN_WAIT
{
ct_filter_add_state(STATE(us_filter),
IPPROTO_TCP,
TCP_CONNTRACK_FIN_WAIT);
__kernel_filter_add_state(TCP_CONNTRACK_FIN_WAIT);
};
tcp_state: T_CLOSE_WAIT
{
ct_filter_add_state(STATE(us_filter),
IPPROTO_TCP,
TCP_CONNTRACK_CLOSE_WAIT);
__kernel_filter_add_state(TCP_CONNTRACK_CLOSE_WAIT);
};
tcp_state: T_LAST_ACK
{
ct_filter_add_state(STATE(us_filter),
IPPROTO_TCP,
TCP_CONNTRACK_LAST_ACK);
__kernel_filter_add_state(TCP_CONNTRACK_LAST_ACK);
};
tcp_state: T_TIME_WAIT
{
ct_filter_add_state(STATE(us_filter),
IPPROTO_TCP,
TCP_CONNTRACK_TIME_WAIT);
__kernel_filter_add_state(TCP_CONNTRACK_TIME_WAIT);
};
tcp_state: T_CLOSE
{
ct_filter_add_state(STATE(us_filter),
IPPROTO_TCP,
TCP_CONNTRACK_CLOSE);
__kernel_filter_add_state(TCP_CONNTRACK_CLOSE);
};
tcp_state: T_LISTEN
{
ct_filter_add_state(STATE(us_filter),
IPPROTO_TCP,
TCP_CONNTRACK_LISTEN);
__kernel_filter_add_state(TCP_CONNTRACK_LISTEN);
};
cache_writethrough: T_WRITE_THROUGH T_ON
{
print_err(CTD_CFG_WARN, "`CacheWriteThrough' clause is obsolete, "
"ignoring");
};
cache_writethrough: T_WRITE_THROUGH T_OFF
{
print_err(CTD_CFG_WARN, "`CacheWriteThrough' clause is obsolete, "
"ignoring");
};
general: T_GENERAL '{' general_list '}';
general_list:
| general_list general_line
;
general_line: hashsize
| hashlimit
| logfile_bool
| logfile_path
| syslog_facility
| syslog_bool
| lock
| unix_line
| netlink_buffer_size
| netlink_buffer_size_max_grown
| family
| event_iterations_limit
| poll_secs
| filter
| netlink_overrun_resync
| netlink_events_reliable
| nice
| scheduler
| systemd
;
systemd: T_SYSTEMD T_ON { conf.systemd = 1; };
systemd: T_SYSTEMD T_OFF { conf.systemd = 0; };
netlink_buffer_size: T_BUFFER_SIZE T_NUMBER
{
conf.netlink_buffer_size = $2;
};
netlink_buffer_size_max_grown : T_BUFFER_SIZE_MAX_GROWN T_NUMBER
{
conf.netlink_buffer_size_max_grown = $2;
};
netlink_overrun_resync : T_NETLINK_OVERRUN_RESYNC T_ON
{
conf.nl_overrun_resync = 30;
};
netlink_overrun_resync : T_NETLINK_OVERRUN_RESYNC T_OFF
{
conf.nl_overrun_resync = -1;
};
netlink_overrun_resync : T_NETLINK_OVERRUN_RESYNC T_NUMBER
{
conf.nl_overrun_resync = $2;
};
netlink_events_reliable : T_NETLINK_EVENTS_RELIABLE T_ON
{
conf.netlink.events_reliable = 1;
};
netlink_events_reliable : T_NETLINK_EVENTS_RELIABLE T_OFF
{
conf.netlink.events_reliable = 0;
};
nice : T_NICE T_SIGNED_NUMBER
{
conf.nice = $2;
};
scheduler : T_SCHEDULER '{' scheduler_options '}';
scheduler_options :
| scheduler_options scheduler_line
;
scheduler_line : T_TYPE T_STRING
{
if (strcasecmp($2, "rr") == 0) {
conf.sched.type = SCHED_RR;
} else if (strcasecmp($2, "fifo") == 0) {
conf.sched.type = SCHED_FIFO;
} else {
print_err(CTD_CFG_ERROR, "unknown scheduler `%s'", $2);
exit(EXIT_FAILURE);
}
};
scheduler_line : T_PRIO T_NUMBER
{
conf.sched.prio = $2;
if (conf.sched.prio < 0 || conf.sched.prio > 99) {
print_err(CTD_CFG_ERROR, "`Priority' must be [0, 99]\n", $2);
exit(EXIT_FAILURE);
}
};
family : T_FAMILY T_STRING
{
print_err(CTD_CFG_WARN, "`Family' is deprecated, ignoring");
};
event_iterations_limit : T_EVENT_ITER_LIMIT T_NUMBER
{
CONFIG(event_iterations_limit) = $2;
};
poll_secs: T_POLL_SECS T_NUMBER
{
conf.flags |= CTD_POLL;
conf.poll_kernel_secs = $2;
if (conf.poll_kernel_secs == 0) {
print_err(CTD_CFG_ERROR, "`PollSecs' clause must be > 0");
exit(EXIT_FAILURE);
}
};
filter : T_FILTER '{' filter_list '}'
{
CONFIG(filter_from_kernelspace) = 0;
};
filter : T_FILTER T_FROM T_USERSPACE '{' filter_list '}'
{
CONFIG(filter_from_kernelspace) = 0;
};
filter : T_FILTER T_FROM T_KERNELSPACE '{' filter_list '}'
{
CONFIG(filter_from_kernelspace) = 1;
};
filter_list :
| filter_list filter_item;
filter_item : T_PROTOCOL T_ACCEPT '{' filter_protocol_list '}'
{
ct_filter_set_logic(STATE(us_filter),
CT_FILTER_L4PROTO,
CT_FILTER_POSITIVE);
__kernel_filter_start();
};
filter_item : T_PROTOCOL T_IGNORE '{' filter_protocol_list '}'
{
ct_filter_set_logic(STATE(us_filter),
CT_FILTER_L4PROTO,
CT_FILTER_NEGATIVE);
__kernel_filter_start();
nfct_filter_set_logic(STATE(filter),
NFCT_FILTER_L4PROTO,
NFCT_FILTER_LOGIC_NEGATIVE);
};
filter_protocol_list :
| filter_protocol_list filter_protocol_item;
filter_protocol_item : T_STRING
{
struct protoent *pent;
pent = getprotobyname($1);
if (pent == NULL) {
print_err(CTD_CFG_WARN, "getprotobyname() cannot find "
"protocol `%s' in /etc/protocols", $1);
break;
}
ct_filter_add_proto(STATE(us_filter), pent->p_proto);
__kernel_filter_start();
nfct_filter_add_attr_u32(STATE(filter),
NFCT_FILTER_L4PROTO,
pent->p_proto);
};
filter_protocol_item : T_TCP
{
struct protoent *pent;
pent = getprotobyname("tcp");
if (pent == NULL) {
print_err(CTD_CFG_WARN, "getprotobyname() cannot find "
"protocol `tcp' in /etc/protocols");
break;
}
ct_filter_add_proto(STATE(us_filter), pent->p_proto);
__kernel_filter_start();
nfct_filter_add_attr_u32(STATE(filter),
NFCT_FILTER_L4PROTO,
pent->p_proto);
};
filter_protocol_item : T_UDP
{
struct protoent *pent;
pent = getprotobyname("udp");
if (pent == NULL) {
print_err(CTD_CFG_WARN, "getprotobyname() cannot find "
"protocol `udp' in /etc/protocols");
break;
}
ct_filter_add_proto(STATE(us_filter), pent->p_proto);
__kernel_filter_start();
nfct_filter_add_attr_u32(STATE(filter),
NFCT_FILTER_L4PROTO,
pent->p_proto);
};
filter_item : T_ADDRESS T_ACCEPT '{' filter_address_list '}'
{
ct_filter_set_logic(STATE(us_filter),
CT_FILTER_ADDRESS,
CT_FILTER_POSITIVE);
__kernel_filter_start();
};
filter_item : T_ADDRESS T_IGNORE '{' filter_address_list '}'
{
ct_filter_set_logic(STATE(us_filter),
CT_FILTER_ADDRESS,
CT_FILTER_NEGATIVE);
__kernel_filter_start();
nfct_filter_set_logic(STATE(filter),
NFCT_FILTER_SRC_IPV4,
NFCT_FILTER_LOGIC_NEGATIVE);
nfct_filter_set_logic(STATE(filter),
NFCT_FILTER_DST_IPV4,
NFCT_FILTER_LOGIC_NEGATIVE);
nfct_filter_set_logic(STATE(filter),
NFCT_FILTER_SRC_IPV6,
NFCT_FILTER_LOGIC_NEGATIVE);
nfct_filter_set_logic(STATE(filter),
NFCT_FILTER_DST_IPV6,
NFCT_FILTER_LOGIC_NEGATIVE);
};
filter_address_list :
| filter_address_list filter_address_item;
filter_address_item : T_IPV4_ADDR T_IP
{
union inet_address ip;
char *slash;
unsigned int cidr = 32;
memset(&ip, 0, sizeof(union inet_address));
slash = strchr($2, '/');
if (slash) {
*slash = '\0';
cidr = atoi(slash+1);
if (cidr > 32) {
print_err(CTD_CFG_WARN, "%s/%d is not a valid network, "
"ignoring", $2, cidr);
break;
}
}
if (!inet_aton($2, &ip.ipv4)) {
print_err(CTD_CFG_WARN, "%s is not a valid IPv4, ignoring", $2);
break;
}
if (slash && cidr < 32) {
/* network byte order */
struct ct_filter_netmask_ipv4 tmp = {
.ip = ip.ipv4,
.mask = ipv4_cidr2mask_net(cidr)
};
if (!ct_filter_add_netmask(STATE(us_filter), &tmp, AF_INET)) {
if (errno == EEXIST)
print_err(CTD_CFG_WARN, "netmask %s is "
"repeated in the "
"ignore pool", $2);
}
} else {
if (!ct_filter_add_ip(STATE(us_filter), &ip, AF_INET)) {
if (errno == EEXIST)
print_err(CTD_CFG_WARN, "IP %s is repeated in "
"the ignore pool", $2);
if (errno == ENOSPC)
print_err(CTD_CFG_WARN, "too many IP in the "
"ignore pool!");
}
}
__kernel_filter_start();
/* host byte order */
struct nfct_filter_ipv4 filter_ipv4 = {
.addr = ntohl(ip.ipv4),
.mask = ipv4_cidr2mask_host(cidr),
};
nfct_filter_add_attr(STATE(filter), NFCT_FILTER_SRC_IPV4, &filter_ipv4);
nfct_filter_add_attr(STATE(filter), NFCT_FILTER_DST_IPV4, &filter_ipv4);
};
filter_address_item : T_IPV6_ADDR T_IP
{
union inet_address ip;
char *slash;
int cidr = 128;
struct nfct_filter_ipv6 filter_ipv6;
memset(&ip, 0, sizeof(union inet_address));
slash = strchr($2, '/');
if (slash) {
*slash = '\0';
cidr = atoi(slash+1);
if (cidr > 128) {
print_err(CTD_CFG_WARN, "%s/%d is not a valid network, "
"ignoring", $2, cidr);
break;
}
}
#ifdef HAVE_INET_PTON_IPV6
if (inet_pton(AF_INET6, $2, &ip.ipv6) <= 0) {
print_err(CTD_CFG_WARN, "%s is not a valid IPv6, ignoring", $2);
break;
}
#else
print_err(CTD_CFG_WARN, "cannot find inet_pton(), IPv6 unsupported!");
break;
#endif
if (slash && cidr < 128) {
struct ct_filter_netmask_ipv6 tmp;
memcpy(tmp.ip, ip.ipv6, sizeof(uint32_t)*4);
ipv6_cidr2mask_net(cidr, tmp.mask);
if (!ct_filter_add_netmask(STATE(us_filter), &tmp, AF_INET6)) {
if (errno == EEXIST)
print_err(CTD_CFG_WARN, "netmask %s is "
"repeated in the "
"ignore pool", $2);
}
} else {
if (!ct_filter_add_ip(STATE(us_filter), &ip, AF_INET6)) {
if (errno == EEXIST)
print_err(CTD_CFG_WARN, "IP %s is repeated in "
"the ignore pool", $2);
if (errno == ENOSPC)
print_err(CTD_CFG_WARN, "too many IP in the "
"ignore pool!");
}
}
__kernel_filter_start();
/* host byte order */
ipv6_addr2addr_host(ip.ipv6, filter_ipv6.addr);
ipv6_cidr2mask_host(cidr, filter_ipv6.mask);
nfct_filter_add_attr(STATE(filter), NFCT_FILTER_SRC_IPV6, &filter_ipv6);
nfct_filter_add_attr(STATE(filter), NFCT_FILTER_DST_IPV6, &filter_ipv6);
};
filter_item : T_STATE T_ACCEPT '{' filter_state_list '}'
{
ct_filter_set_logic(STATE(us_filter),
CT_FILTER_STATE,
CT_FILTER_POSITIVE);
__kernel_filter_start();
};
filter_item : T_STATE T_IGNORE '{' filter_state_list '}'
{
ct_filter_set_logic(STATE(us_filter),
CT_FILTER_STATE,
CT_FILTER_NEGATIVE);
__kernel_filter_start();
nfct_filter_set_logic(STATE(filter),
NFCT_FILTER_L4PROTO_STATE,
NFCT_FILTER_LOGIC_NEGATIVE);
};
filter_state_list :
| filter_state_list filter_state_item;
filter_state_item : tcp_states T_FOR T_TCP;
stats: T_STATS '{' stats_list '}'
{
if (conf.flags & CTD_SYNC_MODE) {
print_err(CTD_CFG_ERROR, "cannot use both `Stats' and `Sync' "
"clauses in conntrackd.conf");
exit(EXIT_FAILURE);
}
conf.flags |= CTD_STATS_MODE;
};
stats_list:
| stats_list stat_line
;
stat_line: stat_logfile_bool
| stat_logfile_path
| stat_syslog_bool
| stat_syslog_facility
| buffer_size
;
stat_logfile_bool : T_LOG T_ON
{
strncpy(conf.stats.logfile, DEFAULT_STATS_LOGFILE, FILENAME_MAXLEN);
};
stat_logfile_bool : T_LOG T_OFF
{
};
stat_logfile_path : T_LOG T_PATH_VAL
{
strncpy(conf.stats.logfile, $2, FILENAME_MAXLEN);
};
stat_syslog_bool : T_SYSLOG T_ON
{
conf.stats.syslog_facility = DEFAULT_SYSLOG_FACILITY;
};
stat_syslog_bool : T_SYSLOG T_OFF
{
conf.stats.syslog_facility = -1;
}
stat_syslog_facility : T_SYSLOG T_STRING
{
if (!strcmp($2, "daemon"))
conf.stats.syslog_facility = LOG_DAEMON;
else if (!strcmp($2, "local0"))
conf.stats.syslog_facility = LOG_LOCAL0;
else if (!strcmp($2, "local1"))
conf.stats.syslog_facility = LOG_LOCAL1;
else if (!strcmp($2, "local2"))
conf.stats.syslog_facility = LOG_LOCAL2;
else if (!strcmp($2, "local3"))
conf.stats.syslog_facility = LOG_LOCAL3;
else if (!strcmp($2, "local4"))
conf.stats.syslog_facility = LOG_LOCAL4;
else if (!strcmp($2, "local5"))
conf.stats.syslog_facility = LOG_LOCAL5;
else if (!strcmp($2, "local6"))
conf.stats.syslog_facility = LOG_LOCAL6;
else if (!strcmp($2, "local7"))
conf.stats.syslog_facility = LOG_LOCAL7;
else {
print_err(CTD_CFG_WARN, "'%s' is not a known syslog facility, "
"ignoring.", $2);
break;
}
if (conf.syslog_facility != -1 &&
conf.stats.syslog_facility != conf.syslog_facility)
print_err(CTD_CFG_WARN, "conflicting Syslog facility "
"values, defaulting to General");
};
buffer_size: T_STAT_BUFFER_SIZE T_NUMBER
{
print_err(CTD_CFG_WARN, "`LogFileBufferSize' is deprecated");
};
helper: T_HELPER '{' helper_list '}'
{
conf.flags |= CTD_HELPER;
};
helper_list:
| helper_list helper_line
;
helper_line: helper_type
;
helper_type: T_TYPE T_STRING T_STRING T_STRING '{' helper_type_list '}'
{
struct ctd_helper_instance *helper_inst;
struct ctd_helper *helper;
struct stack_item *e;
uint16_t l3proto;
uint8_t l4proto;
if (strcmp($3, "inet") == 0)
l3proto = AF_INET;
else if (strcmp($3, "inet6") == 0)
l3proto = AF_INET6;
else {
print_err(CTD_CFG_ERROR, "unknown layer 3 protocol");
exit(EXIT_FAILURE);
}
if (strcmp($4, "tcp") == 0)
l4proto = IPPROTO_TCP;
else if (strcmp($4, "udp") == 0)
l4proto = IPPROTO_UDP;
else {
print_err(CTD_CFG_ERROR, "unknown layer 4 protocol");
exit(EXIT_FAILURE);
}
#ifdef BUILD_CTHELPER
helper = helper_find(CONNTRACKD_LIB_DIR, $2, l4proto, RTLD_NOW);
if (helper == NULL) {
print_err(CTD_CFG_ERROR, "Unknown `%s' helper", $2);
exit(EXIT_FAILURE);
}
#else
print_err(CTD_CFG_ERROR, "Helper support is disabled, recompile "
"conntrackd");
exit(EXIT_FAILURE);
#endif
helper_inst = calloc(1, sizeof(struct ctd_helper_instance));
if (helper_inst == NULL)
break;
helper_inst->l3proto = l3proto;
helper_inst->l4proto = l4proto;
helper_inst->helper = helper;
while ((e = stack_item_pop(&symbol_stack, -1)) != NULL) {
switch(e->type) {
case SYMBOL_HELPER_QUEUE_NUM: {
int *qnum = (int *) &e->data;
helper_inst->queue_num = *qnum;
stack_item_free(e);
break;
}
case SYMBOL_HELPER_QUEUE_LEN: {
int *qlen = (int *) &e->data;
helper_inst->queue_len = *qlen;
stack_item_free(e);
break;
}
case SYMBOL_HELPER_POLICY_EXPECT_ROOT: {
struct ctd_helper_policy *pol =
(struct ctd_helper_policy *) &e->data;
struct ctd_helper_policy *matching = NULL;
int i;
for (i=0; i<CTD_HELPER_POLICY_MAX; i++) {
if (strcmp(helper->policy[i].name,
pol->name) != 0)
continue;
matching = pol;
break;
}
if (matching == NULL) {
print_err(CTD_CFG_ERROR,
"Unknown policy `%s' in helper "
"configuration", pol->name);
exit(EXIT_FAILURE);
}
/* FIXME: First set default policy, then change only
* tuned fields, not everything.
*/
memcpy(&helper->policy[i], pol,
sizeof(struct ctd_helper_policy));
stack_item_free(e);
break;
}
default:
print_err(CTD_CFG_ERROR,
"Unexpected symbol parsing helper policy");
exit(EXIT_FAILURE);
break;
}
}
list_add(&helper_inst->head, &CONFIG(cthelper).list);
};
helper_type_list:
| helper_type_list helper_type_line
;
helper_type_line: helper_type
;
helper_type: T_HELPER_QUEUE_NUM T_NUMBER
{
int *qnum;
struct stack_item *e;
e = stack_item_alloc(SYMBOL_HELPER_QUEUE_NUM, sizeof(int));
qnum = (int *) e->data;
*qnum = $2;
stack_item_push(&symbol_stack, e);
};
helper_type: T_HELPER_QUEUE_LEN T_NUMBER
{
int *qlen;
struct stack_item *e;
e = stack_item_alloc(SYMBOL_HELPER_QUEUE_LEN, sizeof(int));
qlen = (int *) e->data;
*qlen = $2;
stack_item_push(&symbol_stack, e);
};
helper_type: T_HELPER_POLICY T_STRING '{' helper_policy_list '}'
{
struct stack_item *e;
struct ctd_helper_policy *policy;
e = stack_item_pop(&symbol_stack, SYMBOL_HELPER_EXPECT_POLICY_LEAF);
if (e == NULL) {
print_err(CTD_CFG_ERROR,
"Helper policy configuration empty, fix your "
"configuration file, please");
exit(EXIT_FAILURE);
break;
}
policy = (struct ctd_helper_policy *) &e->data;
strncpy(policy->name, $2, CTD_HELPER_NAME_LEN);
policy->name[CTD_HELPER_NAME_LEN-1] = '\0';
/* Now object is complete. */
e->type = SYMBOL_HELPER_POLICY_EXPECT_ROOT;
stack_item_push(&symbol_stack, e);
};
helper_policy_list:
| helper_policy_list helper_policy_line
;
helper_policy_line: helper_policy_expect_max
| helper_policy_expect_timeout
;
helper_policy_expect_max: T_HELPER_EXPECT_MAX T_NUMBER
{
struct stack_item *e;
struct ctd_helper_policy *policy;
e = stack_item_pop(&symbol_stack, SYMBOL_HELPER_EXPECT_POLICY_LEAF);
if (e == NULL) {
e = stack_item_alloc(SYMBOL_HELPER_EXPECT_POLICY_LEAF,
sizeof(struct ctd_helper_policy));
}
policy = (struct ctd_helper_policy *) &e->data;
policy->expect_max = $2;
stack_item_push(&symbol_stack, e);
};
helper_policy_expect_timeout: T_HELPER_EXPECT_TIMEOUT T_NUMBER
{
struct stack_item *e;
struct ctd_helper_policy *policy;
e = stack_item_pop(&symbol_stack, SYMBOL_HELPER_EXPECT_POLICY_LEAF);
if (e == NULL) {
e = stack_item_alloc(SYMBOL_HELPER_EXPECT_POLICY_LEAF,
sizeof(struct ctd_helper_policy));
}
policy = (struct ctd_helper_policy *) &e->data;
policy->expect_timeout = $2;
stack_item_push(&symbol_stack, e);
};
%%
int __attribute__((noreturn))
yyerror(char *msg)
{
print_err(CTD_CFG_ERROR, "parsing config file in "
"line (%d), symbol '%s': %s",
yylineno, yytext, msg);
exit(EXIT_FAILURE);
}
static void print_err(int type, const char *msg, ...)
{
va_list args;
va_start(args, msg);
switch(type) {
case CTD_CFG_ERROR:
fprintf(stderr, "ERROR: ");
break;
case CTD_CFG_WARN:
fprintf(stderr, "WARNING: ");
break;
default:
fprintf(stderr, "?: ");
}
vfprintf(stderr, msg, args);
va_end(args);
fprintf(stderr,"\n");
}
static void __kernel_filter_start(void)
{
if (!STATE(filter)) {
STATE(filter) = nfct_filter_create();
if (!STATE(filter)) {
print_err(CTD_CFG_ERROR, "cannot create ignore pool!");
exit(EXIT_FAILURE);
}
}
}
static void __kernel_filter_add_state(int value)
{
__kernel_filter_start();
struct nfct_filter_proto filter_proto = {
.proto = IPPROTO_TCP,
.state = value
};
nfct_filter_add_attr(STATE(filter),
NFCT_FILTER_L4PROTO_STATE,
&filter_proto);
}
static void __max_dedicated_links_reached(void)
{
if (conf.channel_num >= MULTICHANNEL_MAX) {
print_err(CTD_CFG_ERROR, "too many dedicated links in "
"the configuration file "
"(Maximum: %d)", MULTICHANNEL_MAX);
exit(EXIT_FAILURE);
}
}
int
init_config(char *filename)
{
FILE *fp;
fp = fopen(filename, "r");
if (!fp)
return -1;
/* Zero may be a valid facility */
CONFIG(syslog_facility) = -1;
CONFIG(stats).syslog_facility = -1;
CONFIG(netlink).subsys_id = -1;
/* Initialize list of user-space helpers */
INIT_LIST_HEAD(&CONFIG(cthelper).list);
stack_init(&symbol_stack);
yyrestart(fp);
yyparse();
fclose(fp);
#ifndef BUILD_SYSTEMD
if (CONFIG(systemd) == 1) {
print_err(CTD_CFG_WARN, "systemd runtime support activated but"
" conntrackd was built without support"
" for it. Recompile conntrackd");
}
#endif /* BUILD_SYSTEMD */
/* set to default is not specified */
if (strcmp(CONFIG(lockfile), "") == 0)
strncpy(CONFIG(lockfile), DEFAULT_LOCKFILE, FILENAME_MAXLEN);
/* default to 180 seconds of expiration time: cache entries */
if (CONFIG(cache_timeout) == 0)
CONFIG(cache_timeout) = 180;
/* default to 60 seconds: purge kernel entries */
if (CONFIG(purge_timeout) == 0)
CONFIG(purge_timeout) = 60;
/* default to 60 seconds of refresh time */
if (CONFIG(refresh) == 0)
CONFIG(refresh) = 60;
if (CONFIG(resend_queue_size) == 0)
CONFIG(resend_queue_size) = 131072;
/* default to a window size of 300 packets */
if (CONFIG(window_size) == 0)
CONFIG(window_size) = 300;
if (CONFIG(event_iterations_limit) == 0)
CONFIG(event_iterations_limit) = 100;
/* default number of bucket of the hashtable that are committed in
one run loop. XXX: no option available to tune this value yet. */
if (CONFIG(general).commit_steps == 0)
CONFIG(general).commit_steps = 8192;
/* if overrun, automatically resync with kernel after 30 seconds */
if (CONFIG(nl_overrun_resync) == 0)
CONFIG(nl_overrun_resync) = 30;
/* default to 128 elements in the channel error queue */
if (CONFIG(channelc).error_queue_length == 0)
CONFIG(channelc).error_queue_length = 128;
if (CONFIG(netlink).subsys_id == -1) {
CONFIG(netlink).subsys_id = NFNL_SUBSYS_CTNETLINK;
CONFIG(netlink).groups = NF_NETLINK_CONNTRACK_NEW |
NF_NETLINK_CONNTRACK_UPDATE |
NF_NETLINK_CONNTRACK_DESTROY;
}
return 0;
}