/* * Copyright (c) 2005 Evgeniy Polyakov * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * Based on iptables/utils/nfnl_osf.c. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define OPTDEL ',' #define OSFPDEL ':' #define MAXOPTSTRLEN 128 static struct nf_osf_opt IANA_opts[] = { { .kind = 0, .length = 1,}, { .kind=1, .length=1,}, { .kind=2, .length=4,}, { .kind=3, .length=3,}, { .kind=4, .length=2,}, { .kind=5, .length=1,}, /* SACK length is not defined */ { .kind=6, .length=6,}, { .kind=7, .length=6,}, { .kind=8, .length=10,}, { .kind=9, .length=2,}, { .kind=10, .length=3,}, { .kind=11, .length=1,}, /* CC: Suppose 1 */ { .kind=12, .length=1,}, /* the same */ { .kind=13, .length=1,}, /* and here too */ { .kind=14, .length=3,}, { .kind=15, .length=1,}, /* TCP Alternate Checksum Data. Length is not defined */ { .kind=16, .length=1,}, { .kind=17, .length=1,}, { .kind=18, .length=3,}, { .kind=19, .length=18,}, { .kind=20, .length=1,}, { .kind=21, .length=1,}, { .kind=22, .length=1,}, { .kind=23, .length=1,}, { .kind=24, .length=1,}, { .kind=25, .length=1,}, { .kind=26, .length=1,}, }; static char *nf_osf_strchr(char *ptr, char c) { char *tmp; tmp = strchr(ptr, c); if (tmp) *tmp = '\0'; while (tmp && tmp + 1 && isspace(*(tmp + 1))) tmp++; return tmp; } static void nf_osf_parse_opt(struct nf_osf_opt *opt, __u16 *optnum, char *obuf, int olen) { int i, op; char *ptr, wc; unsigned long val; ptr = &obuf[0]; i = 0; while (ptr != NULL && i < olen && *ptr != 0) { val = 0; op = 0; wc = OSF_WSS_PLAIN; switch (obuf[i]) { case 'N': op = OSFOPT_NOP; ptr = nf_osf_strchr(&obuf[i], OPTDEL); if (ptr) { *ptr = '\0'; ptr++; i += (int)(ptr - &obuf[i]); } else i++; break; case 'S': op = OSFOPT_SACKP; ptr = nf_osf_strchr(&obuf[i], OPTDEL); if (ptr) { *ptr = '\0'; ptr++; i += (int)(ptr - &obuf[i]); } else i++; break; case 'T': op = OSFOPT_TS; ptr = nf_osf_strchr(&obuf[i], OPTDEL); if (ptr) { *ptr = '\0'; ptr++; i += (int)(ptr - &obuf[i]); } else i++; break; case 'W': op = OSFOPT_WSO; ptr = nf_osf_strchr(&obuf[i], OPTDEL); if (ptr) { switch (obuf[i + 1]) { case '%': wc = OSF_WSS_MODULO; break; case 'S': wc = OSF_WSS_MSS; break; case 'T': wc = OSF_WSS_MTU; break; default: wc = OSF_WSS_PLAIN; break; } *ptr = '\0'; ptr++; if (wc) val = strtoul(&obuf[i + 2], NULL, 10); else val = strtoul(&obuf[i + 1], NULL, 10); i += (int)(ptr - &obuf[i]); } else i++; break; case 'M': op = OSFOPT_MSS; ptr = nf_osf_strchr(&obuf[i], OPTDEL); if (ptr) { if (obuf[i + 1] == '%') wc = OSF_WSS_MODULO; *ptr = '\0'; ptr++; if (wc) val = strtoul(&obuf[i + 2], NULL, 10); else val = strtoul(&obuf[i + 1], NULL, 10); i += (int)(ptr - &obuf[i]); } else i++; break; case 'E': op = OSFOPT_EOL; ptr = nf_osf_strchr(&obuf[i], OPTDEL); if (ptr) { *ptr = '\0'; ptr++; i += (int)(ptr - &obuf[i]); } else i++; break; default: op = OSFOPT_EMPTY; ptr = nf_osf_strchr(&obuf[i], OPTDEL); if (ptr) { ptr++; i += (int)(ptr - &obuf[i]); } else i++; break; } if (op != OSFOPT_EMPTY) { opt[*optnum].kind = IANA_opts[op].kind; opt[*optnum].length = IANA_opts[op].length; opt[*optnum].wc.wc = wc; opt[*optnum].wc.val = val; (*optnum)++; } } } static int osf_load_line(char *buffer, int len, int del, struct netlink_ctx *ctx) { int i, cnt = 0; char obuf[MAXOPTSTRLEN]; struct nf_osf_user_finger f; char *pbeg, *pend; struct nlmsghdr *nlh; struct nfgenmsg *nfg; char buf[MNL_SOCKET_BUFFER_SIZE]; memset(&f, 0, sizeof(struct nf_osf_user_finger)); if (ctx->debug_mask & NFT_DEBUG_MNL) nft_print(ctx->octx, "Loading '%s'.\n", buffer); for (i = 0; i < len && buffer[i] != '\0'; ++i) { if (buffer[i] == ':') cnt++; } if (cnt != 8) { if (ctx->debug_mask & NFT_DEBUG_MNL) nft_print(ctx->octx, "Wrong input line '%s': cnt: %d, must be 8, i: %d, must be %d.\n", buffer, cnt, i, len); return -EINVAL; } memset(obuf, 0, sizeof(obuf)); pbeg = buffer; pend = nf_osf_strchr(pbeg, OSFPDEL); if (pend) { *pend = '\0'; if (pbeg[0] == 'S') { f.wss.wc = OSF_WSS_MSS; if (pbeg[1] == '%') f.wss.val = strtoul(&pbeg[2], NULL, 10); else if (pbeg[1] == '*') f.wss.val = 0; else f.wss.val = strtoul(&pbeg[1], NULL, 10); } else if (pbeg[0] == 'T') { f.wss.wc = OSF_WSS_MTU; if (pbeg[1] == '%') f.wss.val = strtoul(&pbeg[2], NULL, 10); else if (pbeg[1] == '*') f.wss.val = 0; else f.wss.val = strtoul(&pbeg[1], NULL, 10); } else if (pbeg[0] == '%') { f.wss.wc = OSF_WSS_MODULO; f.wss.val = strtoul(&pbeg[1], NULL, 10); } else if (isdigit(pbeg[0])) { f.wss.wc = OSF_WSS_PLAIN; f.wss.val = strtoul(&pbeg[0], NULL, 10); } pbeg = pend + 1; } pend = nf_osf_strchr(pbeg, OSFPDEL); if (pend) { *pend = '\0'; f.ttl = strtoul(pbeg, NULL, 10); pbeg = pend + 1; } pend = nf_osf_strchr(pbeg, OSFPDEL); if (pend) { *pend = '\0'; f.df = strtoul(pbeg, NULL, 10); pbeg = pend + 1; } pend = nf_osf_strchr(pbeg, OSFPDEL); if (pend) { *pend = '\0'; f.ss = strtoul(pbeg, NULL, 10); pbeg = pend + 1; } pend = nf_osf_strchr(pbeg, OSFPDEL); if (pend) { *pend = '\0'; cnt = snprintf(obuf, sizeof(obuf), "%s,", pbeg); pbeg = pend + 1; } pend = nf_osf_strchr(pbeg, OSFPDEL); if (pend) { *pend = '\0'; if (pbeg[0] == '@' || pbeg[0] == '*') cnt = snprintf(f.genre, sizeof(f.genre), "%s", pbeg + 1); else cnt = snprintf(f.genre, sizeof(f.genre), "%s", pbeg); pbeg = pend + 1; } pend = nf_osf_strchr(pbeg, OSFPDEL); if (pend) { *pend = '\0'; cnt = snprintf(f.version, sizeof(f.version), "%s", pbeg); pbeg = pend + 1; } pend = nf_osf_strchr(pbeg, OSFPDEL); if (pend) { *pend = '\0'; cnt = snprintf(f.subtype, sizeof(f.subtype), "%s", pbeg); pbeg = pend + 1; } nf_osf_parse_opt(f.opt, &f.opt_num, obuf, sizeof(obuf)); memset(buf, 0, sizeof(buf)); if (del) { nlh = mnl_nlmsg_put_header(buf); nlh->nlmsg_type = (NFNL_SUBSYS_OSF << 8) | OSF_MSG_REMOVE; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; nlh->nlmsg_seq = ctx->seqnum; nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); nfg->nfgen_family = AF_UNSPEC; nfg->version = NFNETLINK_V0; nfg->res_id = 0; } else { nlh = mnl_nlmsg_put_header(buf); nlh->nlmsg_type = (NFNL_SUBSYS_OSF << 8) | OSF_MSG_ADD; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; nlh->nlmsg_seq = ctx->seqnum; nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); nfg->nfgen_family = AF_UNSPEC; nfg->version = NFNETLINK_V0; nfg->res_id = 0; mnl_attr_put(nlh, OSF_ATTR_FINGER, sizeof(struct nf_osf_user_finger), &f); } return nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, 0, NULL); } #define OS_SIGNATURES DEFAULT_INCLUDE_PATH "/nftables/osf/pf.os" int nfnl_osf_load_fingerprints(struct netlink_ctx *ctx, int del) { FILE *inf; int err = 0; char buf[1024]; if (ctx->debug_mask & NFT_DEBUG_MNL) nft_print(ctx->octx, "Opening OS signature file '%s'\n", OS_SIGNATURES); inf = fopen(OS_SIGNATURES, "r"); if (!inf) { if (ctx->debug_mask & NFT_DEBUG_MNL) nft_print(ctx->octx, "Failed to open file '%s'\n", OS_SIGNATURES); return -1; } while (fgets(buf, sizeof(buf), inf)) { int len; if (buf[0] == '#' || buf[0] == '\n' || buf[0] == '\r') continue; len = strlen(buf) - 1; if (len <= 0) continue; buf[len] = '\0'; err = osf_load_line(buf, len, del, ctx); if (err) break; memset(buf, 0, sizeof(buf)); } fclose(inf); return err; }