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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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
62 static struct nfnl_handle *nfnlh;
63 static struct nfnl_subsys_handle *nfnlssh;
64
65 static 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
95 static FILE *osf_log_stream;
96
uloga(const char * f,...)97 static 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
ulog(const char * f,...)111 static 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
xt_osf_strchr(char * ptr,char c)136 static 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 && isspace(*(tmp + 1)))
145 tmp++;
146
147 return tmp;
148 }
149
xt_osf_parse_opt(struct xt_osf_opt * opt,__u16 * optnum,char * obuf,int olen)150 static 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 wc = OSF_WSS_PLAIN;
161 switch (obuf[i]) {
162 case 'N':
163 op = OSFOPT_NOP;
164 ptr = xt_osf_strchr(&obuf[i], OPTDEL);
165 if (ptr) {
166 *ptr = '\0';
167 ptr++;
168 i += (int)(ptr - &obuf[i]);
169 } else
170 i++;
171 break;
172 case 'S':
173 op = OSFOPT_SACKP;
174 ptr = xt_osf_strchr(&obuf[i], OPTDEL);
175 if (ptr) {
176 *ptr = '\0';
177 ptr++;
178 i += (int)(ptr - &obuf[i]);
179 } else
180 i++;
181 break;
182 case 'T':
183 op = OSFOPT_TS;
184 ptr = xt_osf_strchr(&obuf[i], OPTDEL);
185 if (ptr) {
186 *ptr = '\0';
187 ptr++;
188 i += (int)(ptr - &obuf[i]);
189 } else
190 i++;
191 break;
192 case 'W':
193 op = OSFOPT_WSO;
194 ptr = xt_osf_strchr(&obuf[i], OPTDEL);
195 if (ptr) {
196 switch (obuf[i + 1]) {
197 case '%':
198 wc = OSF_WSS_MODULO;
199 break;
200 case 'S':
201 wc = OSF_WSS_MSS;
202 break;
203 case 'T':
204 wc = OSF_WSS_MTU;
205 break;
206 default:
207 wc = OSF_WSS_PLAIN;
208 break;
209 }
210
211 *ptr = '\0';
212 ptr++;
213 if (wc)
214 val = strtoul(&obuf[i + 2], NULL, 10);
215 else
216 val = strtoul(&obuf[i + 1], NULL, 10);
217 i += (int)(ptr - &obuf[i]);
218
219 } else
220 i++;
221 break;
222 case 'M':
223 op = OSFOPT_MSS;
224 ptr = xt_osf_strchr(&obuf[i], OPTDEL);
225 if (ptr) {
226 if (obuf[i + 1] == '%')
227 wc = OSF_WSS_MODULO;
228 *ptr = '\0';
229 ptr++;
230 if (wc)
231 val = strtoul(&obuf[i + 2], NULL, 10);
232 else
233 val = strtoul(&obuf[i + 1], NULL, 10);
234 i += (int)(ptr - &obuf[i]);
235 } else
236 i++;
237 break;
238 case 'E':
239 op = OSFOPT_EOL;
240 ptr = xt_osf_strchr(&obuf[i], OPTDEL);
241 if (ptr) {
242 *ptr = '\0';
243 ptr++;
244 i += (int)(ptr - &obuf[i]);
245 } else
246 i++;
247 break;
248 default:
249 op = OSFOPT_EMPTY;
250 ptr = xt_osf_strchr(&obuf[i], OPTDEL);
251 if (ptr) {
252 ptr++;
253 i += (int)(ptr - &obuf[i]);
254 } else
255 i++;
256 break;
257 }
258
259 if (op != OSFOPT_EMPTY) {
260 opt[*optnum].kind = IANA_opts[op].kind;
261 opt[*optnum].length = IANA_opts[op].length;
262 opt[*optnum].wc.wc = wc;
263 opt[*optnum].wc.val = val;
264 (*optnum)++;
265 }
266 }
267 }
268
osf_load_line(char * buffer,int len,int del)269 static int osf_load_line(char *buffer, int len, int del)
270 {
271 int i, cnt = 0;
272 char obuf[MAXOPTSTRLEN];
273 struct xt_osf_user_finger f;
274 char *pbeg, *pend;
275 char buf[NFNL_HEADER_LEN + NFA_LENGTH(sizeof(struct xt_osf_user_finger))];
276 struct nlmsghdr *nmh = (struct nlmsghdr *) buf;
277
278 memset(&f, 0, sizeof(struct xt_osf_user_finger));
279
280 ulog("Loading '%s'.\n", buffer);
281
282 for (i = 0; i < len && buffer[i] != '\0'; ++i) {
283 if (buffer[i] == ':')
284 cnt++;
285 }
286
287 if (cnt != 8) {
288 ulog("Wrong input line '%s': cnt: %d, must be 8, i: %d, must be %d.\n", buffer, cnt, i, len);
289 return -EINVAL;
290 }
291
292 memset(obuf, 0, sizeof(obuf));
293
294 pbeg = buffer;
295 pend = xt_osf_strchr(pbeg, OSFPDEL);
296 if (pend) {
297 *pend = '\0';
298 if (pbeg[0] == 'S') {
299 f.wss.wc = OSF_WSS_MSS;
300 if (pbeg[1] == '%')
301 f.wss.val = strtoul(&pbeg[2], NULL, 10);
302 else if (pbeg[1] == '*')
303 f.wss.val = 0;
304 else
305 f.wss.val = strtoul(&pbeg[1], NULL, 10);
306 } else if (pbeg[0] == 'T') {
307 f.wss.wc = OSF_WSS_MTU;
308 if (pbeg[1] == '%')
309 f.wss.val = strtoul(&pbeg[2], NULL, 10);
310 else if (pbeg[1] == '*')
311 f.wss.val = 0;
312 else
313 f.wss.val = strtoul(&pbeg[1], NULL, 10);
314 } else if (pbeg[0] == '%') {
315 f.wss.wc = OSF_WSS_MODULO;
316 f.wss.val = strtoul(&pbeg[1], NULL, 10);
317 } else if (isdigit(pbeg[0])) {
318 f.wss.wc = OSF_WSS_PLAIN;
319 f.wss.val = strtoul(&pbeg[0], NULL, 10);
320 }
321
322 pbeg = pend + 1;
323 }
324 pend = xt_osf_strchr(pbeg, OSFPDEL);
325 if (pend) {
326 *pend = '\0';
327 f.ttl = strtoul(pbeg, NULL, 10);
328 pbeg = pend + 1;
329 }
330 pend = xt_osf_strchr(pbeg, OSFPDEL);
331 if (pend) {
332 *pend = '\0';
333 f.df = strtoul(pbeg, NULL, 10);
334 pbeg = pend + 1;
335 }
336 pend = xt_osf_strchr(pbeg, OSFPDEL);
337 if (pend) {
338 *pend = '\0';
339 f.ss = strtoul(pbeg, NULL, 10);
340 pbeg = pend + 1;
341 }
342
343 pend = xt_osf_strchr(pbeg, OSFPDEL);
344 if (pend) {
345 *pend = '\0';
346 i = sizeof(obuf);
347 snprintf(obuf, i, "%.*s,", i - 2, pbeg);
348 pbeg = pend + 1;
349 }
350
351 pend = xt_osf_strchr(pbeg, OSFPDEL);
352 if (pend) {
353 *pend = '\0';
354 i = sizeof(f.genre);
355 if (pbeg[0] == '@' || pbeg[0] == '*')
356 pbeg++;
357 snprintf(f.genre, i, "%.*s", i - 1, pbeg);
358 pbeg = pend + 1;
359 }
360
361 pend = xt_osf_strchr(pbeg, OSFPDEL);
362 if (pend) {
363 *pend = '\0';
364 i = sizeof(f.version);
365 snprintf(f.version, i, "%.*s", i - 1, pbeg);
366 pbeg = pend + 1;
367 }
368
369 pend = xt_osf_strchr(pbeg, OSFPDEL);
370 if (pend) {
371 *pend = '\0';
372 i = sizeof(f.subtype);
373 snprintf(f.subtype, i, "%.*s", i - 1, pbeg);
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,
382 NLM_F_ACK | NLM_F_REQUEST);
383 else
384 nfnl_fill_hdr(nfnlssh, nmh, 0, AF_UNSPEC, 0, OSF_MSG_ADD,
385 NLM_F_ACK | NLM_F_REQUEST | NLM_F_CREATE);
386
387 nfnl_addattr_l(nmh, sizeof(buf), OSF_ATTR_FINGER, &f, sizeof(struct xt_osf_user_finger));
388
389 return nfnl_query(nfnlh, nmh);
390 }
391
osf_load_entries(char * path,int del)392 static int osf_load_entries(char *path, int del)
393 {
394 FILE *inf;
395 int err = 0, lineno = 0;
396 char buf[1024];
397
398 inf = fopen(path, "r");
399 if (!inf) {
400 ulog_err("Failed to open file '%s'", path);
401 return -1;
402 }
403
404 while(fgets(buf, sizeof(buf), inf)) {
405 int len, rc;
406
407 lineno++;
408
409 if (buf[0] == '#' || buf[0] == '\n' || buf[0] == '\r')
410 continue;
411
412 len = strlen(buf) - 1;
413
414 if (len <= 0)
415 continue;
416
417 buf[len] = '\0';
418
419 rc = osf_load_line(buf, len, del);
420 if (rc && (!del || errno != ENOENT)) {
421 ulog_err("Failed to load line %d", lineno);
422 err = rc;
423 }
424
425 memset(buf, 0, sizeof(buf));
426 }
427
428 fclose(inf);
429 return err;
430 }
431
main(int argc,char * argv[])432 int main(int argc, char *argv[])
433 {
434 int ch, del = 0, err;
435 char *fingerprints = NULL;
436
437 while ((ch = getopt(argc, argv, "f:dh")) != -1) {
438 switch (ch) {
439 case 'f':
440 fingerprints = optarg;
441 break;
442 case 'd':
443 del = 1;
444 break;
445 default:
446 fprintf(stderr,
447 "Usage: %s -f fingerprints [-d]\n",
448 argv[0]);
449 return -1;
450 }
451 }
452
453 if (!fingerprints) {
454 err = -ENOENT;
455 ulog("Missing fingerprints file argument.\n");
456 goto err_out_exit;
457 }
458
459 nfnlh = nfnl_open();
460 if (!nfnlh) {
461 err = -EINVAL;
462 ulog_err("Failed to create nfnl handler");
463 goto err_out_exit;
464 }
465
466 #ifndef NFNL_SUBSYS_OSF
467 #define NFNL_SUBSYS_OSF 5
468 #endif
469
470 nfnlssh = nfnl_subsys_open(nfnlh, NFNL_SUBSYS_OSF, OSF_MSG_MAX, 0);
471 if (!nfnlssh) {
472 err = -EINVAL;
473 ulog_err("Faied to create nfnl subsystem");
474 goto err_out_close;
475 }
476
477 err = osf_load_entries(fingerprints, del);
478 if (err)
479 goto err_out_close_subsys;
480
481 nfnl_subsys_close(nfnlssh);
482 nfnl_close(nfnlh);
483
484 return 0;
485
486 err_out_close_subsys:
487 nfnl_subsys_close(nfnlssh);
488 err_out_close:
489 nfnl_close(nfnlh);
490 err_out_exit:
491 return err;
492 }
493