nfnl_osf.c revision e37d45ce390c2f5a7f1e64742b9100ecef0def54
1/*
2 * Copyright (c) 2005 Evgeniy Polyakov <johnpol@2ka.mxt.ru>
3 *
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20#include <sys/types.h>
21#include <sys/socket.h>
22#include <sys/poll.h>
23#include <sys/time.h>
24
25#include <arpa/inet.h>
26
27#include <ctype.h>
28#include <errno.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <stdarg.h>
33#include <time.h>
34#include <unistd.h>
35
36#include <netinet/ip.h>
37#include <netinet/tcp.h>
38
39#include <linux/connector.h>
40#include <linux/types.h>
41#include <linux/netlink.h>
42#include <linux/rtnetlink.h>
43#include <linux/unistd.h>
44
45#include <libnfnetlink/libnfnetlink.h>
46
47#include <linux/netfilter/nfnetlink.h>
48#include <linux/netfilter/xt_osf.h>
49
50#define OPTDEL			','
51#define OSFPDEL 		':'
52#define MAXOPTSTRLEN		128
53
54#ifndef NIPQUAD
55#define NIPQUAD(addr) \
56	((unsigned char *)&addr)[0], \
57	((unsigned char *)&addr)[1], \
58	((unsigned char *)&addr)[2], \
59	((unsigned char *)&addr)[3]
60#endif
61
62static struct nfnl_handle *nfnlh;
63static struct nfnl_subsys_handle *nfnlssh;
64
65static struct xt_osf_opt IANA_opts[] = {
66	{ .kind = 0, .length = 1,},
67	{ .kind=1, .length=1,},
68	{ .kind=2, .length=4,},
69	{ .kind=3, .length=3,},
70	{ .kind=4, .length=2,},
71	{ .kind=5, .length=1,},		/* SACK length is not defined */
72	{ .kind=6, .length=6,},
73	{ .kind=7, .length=6,},
74	{ .kind=8, .length=10,},
75	{ .kind=9, .length=2,},
76	{ .kind=10, .length=3,},
77	{ .kind=11, .length=1,},		/* CC: Suppose 1 */
78	{ .kind=12, .length=1,},		/* the same */
79	{ .kind=13, .length=1,},		/* and here too */
80	{ .kind=14, .length=3,},
81	{ .kind=15, .length=1,},		/* TCP Alternate Checksum Data. Length is not defined */
82	{ .kind=16, .length=1,},
83	{ .kind=17, .length=1,},
84	{ .kind=18, .length=3,},
85	{ .kind=19, .length=18,},
86	{ .kind=20, .length=1,},
87	{ .kind=21, .length=1,},
88	{ .kind=22, .length=1,},
89	{ .kind=23, .length=1,},
90	{ .kind=24, .length=1,},
91	{ .kind=25, .length=1,},
92	{ .kind=26, .length=1,},
93};
94
95static FILE *osf_log_stream;
96
97static void uloga(const char *f, ...)
98{
99	va_list ap;
100
101	if (!osf_log_stream)
102		osf_log_stream = stdout;
103
104	va_start(ap, f);
105	vfprintf(osf_log_stream, f, ap);
106	va_end(ap);
107
108	fflush(osf_log_stream);
109}
110
111static void ulog(const char *f, ...)
112{
113	char str[64];
114	struct tm tm;
115	struct timeval tv;
116	va_list ap;
117
118	if (!osf_log_stream)
119		osf_log_stream = stdout;
120
121	gettimeofday(&tv, NULL);
122	localtime_r((time_t *)&tv.tv_sec, &tm);
123	strftime(str, sizeof(str), "%F %R:%S", &tm);
124
125	fprintf(osf_log_stream, "%s.%lu %ld ", str, tv.tv_usec, syscall(__NR_gettid));
126
127	va_start(ap, f);
128	vfprintf(osf_log_stream, f, ap);
129	va_end(ap);
130
131	fflush(osf_log_stream);
132}
133
134#define ulog_err(f, a...) uloga(f ": %s [%d].\n", ##a, strerror(errno), errno)
135
136static char *xt_osf_strchr(char *ptr, char c)
137{
138	char *tmp;
139
140	tmp = strchr(ptr, c);
141	if (tmp)
142		*tmp = '\0';
143
144	while (tmp && tmp + 1 && isspace(*(tmp + 1)))
145		tmp++;
146
147	return tmp;
148}
149
150static void xt_osf_parse_opt(struct xt_osf_opt *opt, __u16 *optnum, char *obuf, int olen)
151{
152	int i, op;
153	char *ptr, wc;
154	unsigned long val;
155
156	ptr = &obuf[0];
157	i = 0;
158	while (ptr != NULL && i < olen && *ptr != 0) {
159		val = 0;
160		op = 0;
161		wc = OSF_WSS_PLAIN;
162		switch (obuf[i]) {
163		case 'N':
164			op = OSFOPT_NOP;
165			ptr = xt_osf_strchr(&obuf[i], OPTDEL);
166			if (ptr) {
167				*ptr = '\0';
168				ptr++;
169				i += (int)(ptr - &obuf[i]);
170			} else
171				i++;
172			break;
173		case 'S':
174			op = OSFOPT_SACKP;
175			ptr = xt_osf_strchr(&obuf[i], OPTDEL);
176			if (ptr) {
177				*ptr = '\0';
178				ptr++;
179				i += (int)(ptr - &obuf[i]);
180			} else
181				i++;
182			break;
183		case 'T':
184			op = OSFOPT_TS;
185			ptr = xt_osf_strchr(&obuf[i], OPTDEL);
186			if (ptr) {
187				*ptr = '\0';
188				ptr++;
189				i += (int)(ptr - &obuf[i]);
190			} else
191				i++;
192			break;
193		case 'W':
194			op = OSFOPT_WSO;
195			ptr = xt_osf_strchr(&obuf[i], OPTDEL);
196			if (ptr) {
197				switch (obuf[i + 1]) {
198				case '%':
199					wc = OSF_WSS_MODULO;
200					break;
201				case 'S':
202					wc = OSF_WSS_MSS;
203					break;
204				case 'T':
205					wc = OSF_WSS_MTU;
206					break;
207				default:
208					wc = OSF_WSS_PLAIN;
209					break;
210				}
211
212				*ptr = '\0';
213				ptr++;
214				if (wc)
215					val = strtoul(&obuf[i + 2], NULL, 10);
216				else
217					val = strtoul(&obuf[i + 1], NULL, 10);
218				i += (int)(ptr - &obuf[i]);
219
220			} else
221				i++;
222			break;
223		case 'M':
224			op = OSFOPT_MSS;
225			ptr = xt_osf_strchr(&obuf[i], OPTDEL);
226			if (ptr) {
227				if (obuf[i + 1] == '%')
228					wc = OSF_WSS_MODULO;
229				*ptr = '\0';
230				ptr++;
231				if (wc)
232					val = strtoul(&obuf[i + 2], NULL, 10);
233				else
234					val = strtoul(&obuf[i + 1], NULL, 10);
235				i += (int)(ptr - &obuf[i]);
236			} else
237				i++;
238			break;
239		case 'E':
240			op = OSFOPT_EOL;
241			ptr = xt_osf_strchr(&obuf[i], OPTDEL);
242			if (ptr) {
243				*ptr = '\0';
244				ptr++;
245				i += (int)(ptr - &obuf[i]);
246			} else
247				i++;
248			break;
249		default:
250			op = OSFOPT_EMPTY;
251			ptr = xt_osf_strchr(&obuf[i], OPTDEL);
252			if (ptr) {
253				ptr++;
254				i += (int)(ptr - &obuf[i]);
255			} else
256				i++;
257			break;
258		}
259
260		if (op != OSFOPT_EMPTY) {
261			opt[*optnum].kind = IANA_opts[op].kind;
262			opt[*optnum].length = IANA_opts[op].length;
263			opt[*optnum].wc.wc = wc;
264			opt[*optnum].wc.val = val;
265			(*optnum)++;
266		}
267	}
268}
269
270static int osf_load_line(char *buffer, int len, int del)
271{
272	int i, cnt = 0;
273	char obuf[MAXOPTSTRLEN];
274	struct xt_osf_user_finger f;
275	char *pbeg, *pend;
276	char buf[NFNL_HEADER_LEN + NFA_LENGTH(sizeof(struct xt_osf_user_finger))];
277	struct nlmsghdr *nmh = (struct nlmsghdr *) buf;
278
279	memset(&f, 0, sizeof(struct xt_osf_user_finger));
280
281	ulog("Loading '%s'.\n", buffer);
282
283	for (i = 0; i < len && buffer[i] != '\0'; ++i) {
284		if (buffer[i] == ':')
285			cnt++;
286	}
287
288	if (cnt != 8) {
289		ulog("Wrong input line '%s': cnt: %d, must be 8, i: %d, must be %d.\n", buffer, cnt, i, len);
290		return -EINVAL;
291	}
292
293	memset(obuf, 0, sizeof(obuf));
294
295	pbeg = buffer;
296	pend = xt_osf_strchr(pbeg, OSFPDEL);
297	if (pend) {
298		*pend = '\0';
299		if (pbeg[0] == 'S') {
300			f.wss.wc = OSF_WSS_MSS;
301			if (pbeg[1] == '%')
302				f.wss.val = strtoul(&pbeg[2], NULL, 10);
303			else if (pbeg[1] == '*')
304				f.wss.val = 0;
305			else
306				f.wss.val = strtoul(&pbeg[1], NULL, 10);
307		} else if (pbeg[0] == 'T') {
308			f.wss.wc = OSF_WSS_MTU;
309			if (pbeg[1] == '%')
310				f.wss.val = strtoul(&pbeg[2], NULL, 10);
311			else if (pbeg[1] == '*')
312				f.wss.val = 0;
313			else
314				f.wss.val = strtoul(&pbeg[1], NULL, 10);
315		} else if (pbeg[0] == '%') {
316			f.wss.wc = OSF_WSS_MODULO;
317			f.wss.val = strtoul(&pbeg[1], NULL, 10);
318		} else if (isdigit(pbeg[0])) {
319			f.wss.wc = OSF_WSS_PLAIN;
320			f.wss.val = strtoul(&pbeg[0], NULL, 10);
321		}
322
323		pbeg = pend + 1;
324	}
325	pend = xt_osf_strchr(pbeg, OSFPDEL);
326	if (pend) {
327		*pend = '\0';
328		f.ttl = strtoul(pbeg, NULL, 10);
329		pbeg = pend + 1;
330	}
331	pend = xt_osf_strchr(pbeg, OSFPDEL);
332	if (pend) {
333		*pend = '\0';
334		f.df = strtoul(pbeg, NULL, 10);
335		pbeg = pend + 1;
336	}
337	pend = xt_osf_strchr(pbeg, OSFPDEL);
338	if (pend) {
339		*pend = '\0';
340		f.ss = strtoul(pbeg, NULL, 10);
341		pbeg = pend + 1;
342	}
343
344	pend = xt_osf_strchr(pbeg, OSFPDEL);
345	if (pend) {
346		*pend = '\0';
347		cnt = snprintf(obuf, sizeof(obuf), "%s,", pbeg);
348		pbeg = pend + 1;
349	}
350
351	pend = xt_osf_strchr(pbeg, OSFPDEL);
352	if (pend) {
353		*pend = '\0';
354		if (pbeg[0] == '@' || pbeg[0] == '*')
355			cnt = snprintf(f.genre, sizeof(f.genre), "%s", pbeg + 1);
356		else
357			cnt = snprintf(f.genre, sizeof(f.genre), "%s", pbeg);
358		pbeg = pend + 1;
359	}
360
361	pend = xt_osf_strchr(pbeg, OSFPDEL);
362	if (pend) {
363		*pend = '\0';
364		cnt = snprintf(f.version, sizeof(f.version), "%s", pbeg);
365		pbeg = pend + 1;
366	}
367
368	pend = xt_osf_strchr(pbeg, OSFPDEL);
369	if (pend) {
370		*pend = '\0';
371		cnt =
372		    snprintf(f.subtype, sizeof(f.subtype), "%s", pbeg);
373		pbeg = pend + 1;
374	}
375
376	xt_osf_parse_opt(f.opt, &f.opt_num, obuf, sizeof(obuf));
377
378	memset(buf, 0, sizeof(buf));
379
380	if (del)
381		nfnl_fill_hdr(nfnlssh, nmh, 0, AF_UNSPEC, 0, OSF_MSG_REMOVE, NLM_F_REQUEST);
382	else
383		nfnl_fill_hdr(nfnlssh, nmh, 0, AF_UNSPEC, 0, OSF_MSG_ADD, NLM_F_REQUEST | NLM_F_CREATE);
384
385	nfnl_addattr_l(nmh, sizeof(buf), OSF_ATTR_FINGER, &f, sizeof(struct xt_osf_user_finger));
386
387	return nfnl_talk(nfnlh, nmh, 0, 0, NULL, NULL, NULL);
388}
389
390static int osf_load_entries(char *path, int del)
391{
392	FILE *inf;
393	int err = 0;
394	char buf[1024];
395
396	inf = fopen(path, "r");
397	if (!inf) {
398		ulog_err("Failed to open file '%s'", path);
399		return -1;
400	}
401
402	while(fgets(buf, sizeof(buf), inf)) {
403		int len;
404
405		if (buf[0] == '#' || buf[0] == '\n' || buf[0] == '\r')
406			continue;
407
408		len = strlen(buf) - 1;
409
410		if (len <= 0)
411			continue;
412
413		buf[len] = '\0';
414
415		err = osf_load_line(buf, len, del);
416		if (err)
417			break;
418
419		memset(buf, 0, sizeof(buf));
420	}
421
422	fclose(inf);
423	return err;
424}
425
426int main(int argc, char *argv[])
427{
428	int ch, del = 0, err;
429	char *fingerprints = NULL;
430
431	while ((ch = getopt(argc, argv, "f:dh")) != -1) {
432		switch (ch) {
433			case 'f':
434				fingerprints = optarg;
435				break;
436			case 'd':
437				del = 1;
438				break;
439			default:
440				fprintf(stderr,
441					"Usage: %s -f fingerprints -d <del rules> -h\n",
442					argv[0]);
443				return -1;
444		}
445	}
446
447	if (!fingerprints) {
448		err = -ENOENT;
449		goto err_out_exit;
450	}
451
452	nfnlh = nfnl_open();
453	if (!nfnlh) {
454		err = -EINVAL;
455		ulog_err("Failed to create nfnl handler");
456		goto err_out_exit;
457	}
458
459#ifndef NFNL_SUBSYS_OSF
460#define NFNL_SUBSYS_OSF	5
461#endif
462
463	nfnlssh = nfnl_subsys_open(nfnlh, NFNL_SUBSYS_OSF, OSF_MSG_MAX, 0);
464	if (!nfnlssh) {
465		err = -EINVAL;
466		ulog_err("Faied to create nfnl subsystem");
467		goto err_out_close;
468	}
469
470	err = osf_load_entries(fingerprints, del);
471	if (err)
472		goto err_out_close_subsys;
473
474	nfnl_subsys_close(nfnlssh);
475	nfnl_close(nfnlh);
476
477	return 0;
478
479err_out_close_subsys:
480	nfnl_subsys_close(nfnlssh);
481err_out_close:
482	nfnl_close(nfnlh);
483err_out_exit:
484	return err;
485}
486