libipt_LOG.c revision f46e1afd45c6d735c8bb8f5a67286780ff40be6a
1/* Shared library add-on to iptables to add LOG support. */
2#include <stdio.h>
3#include <netdb.h>
4#include <string.h>
5#include <stdlib.h>
6#include <syslog.h>
7#include <getopt.h>
8#include <iptables.h>
9#include <linux/netfilter_ipv4/ip_tables.h>
10#include <linux/netfilter_ipv4/ipt_LOG.h>
11
12#define LOG_DEFAULT_LEVEL LOG_WARNING
13
14/* Function which prints out usage message. */
15static void
16help(void)
17{
18	printf(
19"LOG v%s options:\n"
20" --log-level level		Level of logging (numeric or see syslog.conf)\n"
21" --log-prefix prefix		Prefix log messages with this prefix.\n\n"
22" --log-tcp-sequence		Log TCP sequence numbers.\n\n"
23" --log-tcp-options		Log TCP options.\n\n"
24" --log-ip-options		Log IP options.\n\n"
25" --log-uid			Log UID owning the local socket.\n\n",
26IPTABLES_VERSION);
27}
28
29static struct option opts[] = {
30	{ .name = "log-level",        .has_arg = 1, .flag = 0, .val = '!' },
31	{ .name = "log-prefix",       .has_arg = 1, .flag = 0, .val = '#' },
32	{ .name = "log-tcp-sequence", .has_arg = 0, .flag = 0, .val = '1' },
33	{ .name = "log-tcp-options",  .has_arg = 0, .flag = 0, .val = '2' },
34	{ .name = "log-ip-options",   .has_arg = 0, .flag = 0, .val = '3' },
35	{ .name = "log-uid",          .has_arg = 0, .flag = 0, .val = '4' },
36	{ .name = 0 }
37};
38
39/* Initialize the target. */
40static void
41init(struct ipt_entry_target *t, unsigned int *nfcache)
42{
43	struct ipt_log_info *loginfo = (struct ipt_log_info *)t->data;
44
45	loginfo->level = LOG_DEFAULT_LEVEL;
46
47	/* Can't cache this */
48	*nfcache |= NFC_UNKNOWN;
49}
50
51struct ipt_log_names {
52	const char *name;
53	unsigned int level;
54};
55
56static struct ipt_log_names ipt_log_names[]
57= { { .name = "alert",   .level = LOG_ALERT },
58    { .name = "crit",    .level = LOG_CRIT },
59    { .name = "debug",   .level = LOG_DEBUG },
60    { .name = "emerg",   .level = LOG_EMERG },
61    { .name = "error",   .level = LOG_ERR },		/* DEPRECATED */
62    { .name = "info",    .level = LOG_INFO },
63    { .name = "notice",  .level = LOG_NOTICE },
64    { .name = "panic",   .level = LOG_EMERG },		/* DEPRECATED */
65    { .name = "warning", .level = LOG_WARNING }
66};
67
68static u_int8_t
69parse_level(const char *level)
70{
71	unsigned int lev = -1;
72	unsigned int set = 0;
73
74	if (string_to_number(level, 0, 7, &lev) == -1) {
75		unsigned int i = 0;
76
77		for (i = 0;
78		     i < sizeof(ipt_log_names) / sizeof(struct ipt_log_names);
79		     i++) {
80			if (strncasecmp(level, ipt_log_names[i].name,
81					strlen(level)) == 0) {
82				if (set++)
83					exit_error(PARAMETER_PROBLEM,
84						   "log-level `%s' ambiguous",
85						   level);
86				lev = ipt_log_names[i].level;
87			}
88		}
89
90		if (!set)
91			exit_error(PARAMETER_PROBLEM,
92				   "log-level `%s' unknown", level);
93	}
94
95	return (u_int8_t)lev;
96}
97
98#define IPT_LOG_OPT_LEVEL 0x01
99#define IPT_LOG_OPT_PREFIX 0x02
100#define IPT_LOG_OPT_TCPSEQ 0x04
101#define IPT_LOG_OPT_TCPOPT 0x08
102#define IPT_LOG_OPT_IPOPT 0x10
103#define IPT_LOG_OPT_UID 0x20
104
105/* Function which parses command options; returns true if it
106   ate an option */
107static int
108parse(int c, char **argv, int invert, unsigned int *flags,
109      const struct ipt_entry *entry,
110      struct ipt_entry_target **target)
111{
112	struct ipt_log_info *loginfo = (struct ipt_log_info *)(*target)->data;
113
114	switch (c) {
115	case '!':
116		if (*flags & IPT_LOG_OPT_LEVEL)
117			exit_error(PARAMETER_PROBLEM,
118				   "Can't specify --log-level twice");
119
120		if (check_inverse(optarg, &invert, NULL, 0))
121			exit_error(PARAMETER_PROBLEM,
122				   "Unexpected `!' after --log-level");
123
124		loginfo->level = parse_level(optarg);
125		*flags |= IPT_LOG_OPT_LEVEL;
126		break;
127
128	case '#':
129		if (*flags & IPT_LOG_OPT_PREFIX)
130			exit_error(PARAMETER_PROBLEM,
131				   "Can't specify --log-prefix twice");
132
133		if (check_inverse(optarg, &invert, NULL, 0))
134			exit_error(PARAMETER_PROBLEM,
135				   "Unexpected `!' after --log-prefix");
136
137		if (strlen(optarg) > sizeof(loginfo->prefix) - 1)
138			exit_error(PARAMETER_PROBLEM,
139				   "Maximum prefix length %u for --log-prefix",
140				   (unsigned int)sizeof(loginfo->prefix) - 1);
141
142		strcpy(loginfo->prefix, optarg);
143		*flags |= IPT_LOG_OPT_PREFIX;
144		break;
145
146	case '1':
147		if (*flags & IPT_LOG_OPT_TCPSEQ)
148			exit_error(PARAMETER_PROBLEM,
149				   "Can't specify --log-tcp-sequence "
150				   "twice");
151
152		loginfo->logflags |= IPT_LOG_TCPSEQ;
153		*flags |= IPT_LOG_OPT_TCPSEQ;
154		break;
155
156	case '2':
157		if (*flags & IPT_LOG_OPT_TCPOPT)
158			exit_error(PARAMETER_PROBLEM,
159				   "Can't specify --log-tcp-options twice");
160
161		loginfo->logflags |= IPT_LOG_TCPOPT;
162		*flags |= IPT_LOG_OPT_TCPOPT;
163		break;
164
165	case '3':
166		if (*flags & IPT_LOG_OPT_IPOPT)
167			exit_error(PARAMETER_PROBLEM,
168				   "Can't specify --log-ip-options twice");
169
170		loginfo->logflags |= IPT_LOG_IPOPT;
171		*flags |= IPT_LOG_OPT_IPOPT;
172		break;
173
174	case '4':
175		if (*flags & IPT_LOG_OPT_UID)
176			exit_error(PARAMETER_PROBLEM,
177				   "Can't specify --log-uid twice");
178
179		loginfo->logflags |= IPT_LOG_UID;
180		*flags |= IPT_LOG_OPT_UID;
181		break;
182
183	default:
184		return 0;
185	}
186
187	return 1;
188}
189
190/* Final check; nothing. */
191static void final_check(unsigned int flags)
192{
193}
194
195/* Prints out the targinfo. */
196static void
197print(const struct ipt_ip *ip,
198      const struct ipt_entry_target *target,
199      int numeric)
200{
201	const struct ipt_log_info *loginfo
202		= (const struct ipt_log_info *)target->data;
203	unsigned int i = 0;
204
205	printf("LOG ");
206	if (numeric)
207		printf("flags %u level %u ",
208		       loginfo->logflags, loginfo->level);
209	else {
210		for (i = 0;
211		     i < sizeof(ipt_log_names) / sizeof(struct ipt_log_names);
212		     i++) {
213			if (loginfo->level == ipt_log_names[i].level) {
214				printf("level %s ", ipt_log_names[i].name);
215				break;
216			}
217		}
218		if (i == sizeof(ipt_log_names) / sizeof(struct ipt_log_names))
219			printf("UNKNOWN level %u ", loginfo->level);
220		if (loginfo->logflags & IPT_LOG_TCPSEQ)
221			printf("tcp-sequence ");
222		if (loginfo->logflags & IPT_LOG_TCPOPT)
223			printf("tcp-options ");
224		if (loginfo->logflags & IPT_LOG_IPOPT)
225			printf("ip-options ");
226		if (loginfo->logflags & IPT_LOG_UID)
227			printf("uid ");
228		if (loginfo->logflags & ~(IPT_LOG_MASK))
229			printf("unknown-flags ");
230	}
231
232	if (strcmp(loginfo->prefix, "") != 0)
233		printf("prefix `%s' ", loginfo->prefix);
234}
235
236/* Saves the union ipt_targinfo in parsable form to stdout. */
237static void
238save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
239{
240	const struct ipt_log_info *loginfo
241		= (const struct ipt_log_info *)target->data;
242
243	if (strcmp(loginfo->prefix, "") != 0)
244		printf("--log-prefix \"%s\" ", loginfo->prefix);
245
246	if (loginfo->level != LOG_DEFAULT_LEVEL)
247		printf("--log-level %d ", loginfo->level);
248
249	if (loginfo->logflags & IPT_LOG_TCPSEQ)
250		printf("--log-tcp-sequence ");
251	if (loginfo->logflags & IPT_LOG_TCPOPT)
252		printf("--log-tcp-options ");
253	if (loginfo->logflags & IPT_LOG_IPOPT)
254		printf("--log-ip-options ");
255	if (loginfo->logflags & IPT_LOG_UID)
256		printf("--log-uid ");
257}
258
259static
260struct iptables_target log
261= {
262    .name          = "LOG",
263    .version       = IPTABLES_VERSION,
264    .size          = IPT_ALIGN(sizeof(struct ipt_log_info)),
265    .userspacesize = IPT_ALIGN(sizeof(struct ipt_log_info)),
266    .help          = &help,
267    .init          = &init,
268    .parse         = &parse,
269    .final_check   = &final_check,
270    .print         = &print,
271    .save          = &save,
272    .extra_opts    = opts
273};
274
275void _init(void)
276{
277	register_target(&log);
278}
279