Message ID | 20180810130200.704-3-ffmancera@riseup.net |
---|---|
State | Changes Requested |
Delegated to: | Pablo Neira |
Headers | show |
Series | [1/3,nft] files: osf: copy iptables/utils/pf.os into nftables tree | expand |
I think we should place osf_init in nfnl_osf.h so this way we don't need to include osf.h in rule.c. If you agree I will send another patchset iteration. Thanks. El 10 de agosto de 2018 15:02:00 CEST, Fernando Fernandez Mancera <ffmancera@riseup.net> escribió: >Import iptables/utils/nfnl_osf.c into nftables tree with some changes >in order >to load OS fingerprints automatically from pf.os file. > >Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net> >--- > include/linux/netfilter/nfnetlink_osf.h | 119 +++++++ > include/nfnl_osf.h | 6 + > include/osf.h | 2 + > src/Makefile.am | 1 + > src/nfnl_osf.c | 449 ++++++++++++++++++++++++ > src/osf.c | 2 + > src/rule.c | 5 + > 7 files changed, 584 insertions(+) > create mode 100644 include/linux/netfilter/nfnetlink_osf.h > create mode 100644 include/nfnl_osf.h > create mode 100644 src/nfnl_osf.c > >diff --git a/include/linux/netfilter/nfnetlink_osf.h >b/include/linux/netfilter/nfnetlink_osf.h >new file mode 100644 >index 0000000..15a39d2 >--- /dev/null >+++ b/include/linux/netfilter/nfnetlink_osf.h >@@ -0,0 +1,119 @@ >+#ifndef _NF_OSF_H >+#define _NF_OSF_H >+ >+#include <linux/types.h> >+ >+#define MAXGENRELEN 32 >+ >+#define NF_OSF_GENRE (1 << 0) >+#define NF_OSF_TTL (1 << 1) >+#define NF_OSF_LOG (1 << 2) >+#define NF_OSF_INVERT (1 << 3) >+ >+#define NF_OSF_LOGLEVEL_ALL 0 /* log all matched fingerprints */ >+#define NF_OSF_LOGLEVEL_FIRST 1 /* log only the first matced >fingerprint */ >+#define NF_OSF_LOGLEVEL_ALL_KNOWN 2 /* do not log unknown packets */ >+ >+#define NF_OSF_TTL_TRUE 0 /* True ip and fingerprint TTL comparison >*/ >+ >+/* Check if ip TTL is less than fingerprint one */ >+#define NF_OSF_TTL_LESS 1 >+ >+/* Do not compare ip and fingerprint TTL at all */ >+#define NF_OSF_TTL_NOCHECK 2 >+ >+#define NF_OSF_FLAGMASK (NF_OSF_GENRE | NF_OSF_TTL | \ >+ NF_OSF_LOG | NF_OSF_INVERT) >+/* Wildcard MSS (kind of). >+ * It is used to implement a state machine for the different wildcard >values >+ * of the MSS and window sizes. >+ */ >+struct nf_osf_wc { >+ __u32 wc; >+ __u32 val; >+}; >+ >+/* This struct represents IANA options >+ * http://www.iana.org/assignments/tcp-parameters >+ */ >+struct nf_osf_opt { >+ __u16 kind, length; >+ struct nf_osf_wc wc; >+}; >+ >+struct nf_osf_info { >+ char genre[MAXGENRELEN]; >+ __u32 len; >+ __u32 flags; >+ __u32 loglevel; >+ __u32 ttl; >+}; >+ >+struct nf_osf_user_finger { >+ struct nf_osf_wc wss; >+ >+ __u8 ttl, df; >+ __u16 ss, mss; >+ __u16 opt_num; >+ >+ char genre[MAXGENRELEN]; >+ char version[MAXGENRELEN]; >+ char subtype[MAXGENRELEN]; >+ >+ /* MAX_IPOPTLEN is maximum if all options are NOPs or EOLs */ >+ struct nf_osf_opt opt[MAX_IPOPTLEN]; >+}; >+ >+struct nf_osf_nlmsg { >+ struct nf_osf_user_finger f; >+ struct iphdr ip; >+ struct tcphdr tcp; >+}; >+ >+/* Defines for IANA option kinds */ >+enum iana_options { >+ OSFOPT_EOL = 0, /* End of options */ >+ OSFOPT_NOP, /* NOP */ >+ OSFOPT_MSS, /* Maximum segment size */ >+ OSFOPT_WSO, /* Window scale option */ >+ OSFOPT_SACKP, /* SACK permitted */ >+ OSFOPT_SACK, /* SACK */ >+ OSFOPT_ECHO, >+ OSFOPT_ECHOREPLY, >+ OSFOPT_TS, /* Timestamp option */ >+ OSFOPT_POCP, /* Partial Order Connection Permitted */ >+ OSFOPT_POSP, /* Partial Order Service Profile */ >+ >+ /* Others are not used in the current OSF */ >+ OSFOPT_EMPTY = 255, >+}; >+ >+/* >+ * Initial window size option state machine: multiple of mss, mtu or >+ * plain numeric value. Can also be made as plain numeric value which >+ * is not a multiple of specified value. >+ */ >+enum nf_osf_window_size_options { >+ OSF_WSS_PLAIN = 0, >+ OSF_WSS_MSS, >+ OSF_WSS_MTU, >+ OSF_WSS_MODULO, >+ OSF_WSS_MAX, >+}; >+ >+enum nf_osf_attr_type { >+ OSF_ATTR_UNSPEC, >+ OSF_ATTR_FINGER, >+ OSF_ATTR_MAX, >+}; >+ >+/* >+ * Add/remove fingerprint from the kernel. >+ */ >+enum nf_osf_msg_types { >+ OSF_MSG_ADD, >+ OSF_MSG_REMOVE, >+ OSF_MSG_MAX, >+}; >+ >+#endif /* _NF_OSF_H */ >diff --git a/include/nfnl_osf.h b/include/nfnl_osf.h >new file mode 100644 >index 0000000..d9287e9 >--- /dev/null >+++ b/include/nfnl_osf.h >@@ -0,0 +1,6 @@ >+#ifndef _NFNL_OSF_H >+#define _NFNL_OSF_H >+ >+int nfnl_osf_load_fingerprints(struct netlink_ctx *ctx, int del); >+ >+#endif /* _NFNL_OSF_H */ >diff --git a/include/osf.h b/include/osf.h >index 715b04e..0a35b07 100644 >--- a/include/osf.h >+++ b/include/osf.h >@@ -1,6 +1,8 @@ > #ifndef NFTABLES_OSF_H > #define NFTABLES_OSF_H > >+bool osf_init; >+ > struct expr *osf_expr_alloc(const struct location *loc); > > #endif /* NFTABLES_OSF_H */ >diff --git a/src/Makefile.am b/src/Makefile.am >index ed3640e..e569029 100644 >--- a/src/Makefile.am >+++ b/src/Makefile.am >@@ -57,6 +57,7 @@ libnftables_la_SOURCES = \ > services.c \ > mergesort.c \ > osf.c \ >+ nfnl_osf.c \ > tcpopt.c \ > socket.c \ > libnftables.c >diff --git a/src/nfnl_osf.c b/src/nfnl_osf.c >new file mode 100644 >index 0000000..07bf682 >--- /dev/null >+++ b/src/nfnl_osf.c >@@ -0,0 +1,449 @@ >+/* >+ * Copyright (c) 2005 Evgeniy Polyakov <johnpol@2ka.mxt.ru> >+ * >+ * >+ * 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. >+ */ >+ >+#include <sys/time.h> >+ >+#include <ctype.h> >+#include <errno.h> >+#include <stdlib.h> >+#include <string.h> >+#include <time.h> >+ >+#include <netinet/ip.h> >+#include <netinet/tcp.h> >+ >+#include <linux/unistd.h> >+ >+#include <libmnl/libmnl.h> >+ >+#include <linux/netfilter/nfnetlink.h> >+#include <linux/netfilter/nfnetlink_osf.h> >+#include <mnl.h> >+#include <nfnl_osf.h> >+ >+#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 void uloga(const char *f, struct netlink_ctx *ctx, ...) >+{ >+ if (!(ctx->debug_mask & NFT_DEBUG_NETLINK)) >+ return; >+ >+ nft_print(ctx->octx, "%s", f); >+} >+ >+static void ulog(const char *f, struct netlink_ctx *ctx, ...) >+{ >+ char str[64]; >+ struct tm tm; >+ struct timeval tv; >+ >+ gettimeofday(&tv, NULL); >+ localtime_r((time_t *)&tv.tv_sec, &tm); >+ strftime(str, sizeof(str), "%F %R:%S", &tm); >+ >+ if (!(ctx->debug_mask & NFT_DEBUG_NETLINK)) >+ return; >+ >+ nft_print(ctx->octx, "%s.%lu %ld %s", str, tv.tv_usec, >+ syscall(__NR_gettid), f); >+} >+ >+#define ulog_err(f, ctx, a...) uloga(f ": %s [%d].\n", ctx, ##a, >strerror(errno), errno) >+ >+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 >mnl_socket *nl, >+ 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)); >+ >+ ulog("Loading '%s'.\n", ctx, buffer); >+ >+ for (i = 0; i < len && buffer[i] != '\0'; ++i) { >+ if (buffer[i] == ':') >+ cnt++; >+ } >+ >+ if (cnt != 8) { >+ ulog("Wrong input line '%s': cnt: %d, must be 8, i: %d, must be >%d.\n", ctx, 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 = nftnl_nlmsg_build_hdr(buf, (NFNL_SUBSYS_OSF << 8) | >+ OSF_MSG_REMOVE, AF_UNSPEC, >+ NLM_F_REQUEST | NLM_F_ACK, >+ 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 = nftnl_nlmsg_build_hdr(buf, (NFNL_SUBSYS_OSF << 8) | >+ OSF_MSG_ADD, AF_UNSPEC, >+ NLM_F_REQUEST | NLM_F_CREATE | >+ NLM_F_ACK, 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); >+} >+ >+static int osf_load_entries(const char *path, int del, struct >mnl_socket *nl, >+ struct netlink_ctx *ctx) >+{ >+ FILE *inf; >+ int err = 0; >+ char buf[1024]; >+ >+ inf = fopen(path, "r"); >+ if (!inf) { >+ ulog_err("Failed to open file '%s'", ctx, path); >+ 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, nl, ctx); >+ if (err) >+ break; >+ >+ memset(buf, 0, sizeof(buf)); >+ } >+ >+ fclose(inf); >+ return err; >+} >+ >+int nfnl_osf_load_fingerprints(struct netlink_ctx *ctx, int del) >+{ >+ int err; >+ struct mnl_socket *nl; >+ const char *fingerprints = "files/osf/pf.os"; >+ >+ if (!fingerprints) { >+ err = -ENOENT; >+ goto err_out_exit; >+ } >+ >+ nl = mnl_socket_open(NETLINK_NETFILTER); >+ if (nl == NULL) { >+ err = -EINVAL; >+ ulog_err("Failed to open mnl socket", ctx); >+ goto err_out_exit; >+ } >+ >+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { >+ err = -EINVAL; >+ ulog_err("Failed to bind mnl socket", ctx); >+ goto err_out_exit; >+ } >+ >+#ifndef NFNL_SUBSYS_OSF >+#define NFNL_SUBSYS_OSF 5 >+#endif >+ >+ err = osf_load_entries(fingerprints, del, nl, ctx); >+ if (err < 0) >+ goto err_out_close; >+ >+ return 0; >+ >+err_out_close: >+ mnl_socket_close(nl); >+err_out_exit: >+ return err; >+} >diff --git a/src/osf.c b/src/osf.c >index 131d54e..210bfbe 100644 >--- a/src/osf.c >+++ b/src/osf.c >@@ -3,6 +3,7 @@ > #include <utils.h> > #include <string.h> > #include <osf.h> >+#include <nfnl_osf.h> > >static void osf_expr_print(const struct expr *expr, struct output_ctx >*octx) > { >@@ -26,6 +27,7 @@ struct expr *osf_expr_alloc(const struct location >*loc) > const struct datatype *type = &string_type; > struct expr *expr; > >+ osf_init = true; > expr = expr_alloc(loc, &osf_expr_ops, type, > BYTEORDER_HOST_ENDIAN, len); > >diff --git a/src/rule.c b/src/rule.c >index 7a7ac73..dc1ce8b 100644 >--- a/src/rule.c >+++ b/src/rule.c >@@ -22,6 +22,8 @@ > #include <netdb.h> > #include <netlink.h> > #include <json.h> >+#include <nfnl_osf.h> >+#include <osf.h> > > #include <libnftnl/common.h> > #include <libnftnl/ruleset.h> >@@ -2135,6 +2137,9 @@ int do_command(struct netlink_ctx *ctx, struct >cmd *cmd) > default: > BUG("invalid command object type %u\n", cmd->obj); > } >+ >+ if (osf_init) >+ nfnl_osf_load_fingerprints(ctx, 0); > } > > static int payload_match_stmt_cmp(const void *p1, const void *p2)
On Fri, Aug 10, 2018 at 03:02:00PM +0200, Fernando Fernandez Mancera wrote: > Import iptables/utils/nfnl_osf.c into nftables tree with some changes in order > to load OS fingerprints automatically from pf.os file. > > Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net> > --- > include/linux/netfilter/nfnetlink_osf.h | 119 +++++++ > include/nfnl_osf.h | 6 + > include/osf.h | 2 + > src/Makefile.am | 1 + > src/nfnl_osf.c | 449 ++++++++++++++++++++++++ > src/osf.c | 2 + > src/rule.c | 5 + > 7 files changed, 584 insertions(+) > create mode 100644 include/linux/netfilter/nfnetlink_osf.h > create mode 100644 include/nfnl_osf.h > create mode 100644 src/nfnl_osf.c > > diff --git a/include/linux/netfilter/nfnetlink_osf.h b/include/linux/netfilter/nfnetlink_osf.h > new file mode 100644 > index 0000000..15a39d2 > --- /dev/null > +++ b/include/linux/netfilter/nfnetlink_osf.h > @@ -0,0 +1,119 @@ > +#ifndef _NF_OSF_H > +#define _NF_OSF_H > + > +#include <linux/types.h> > + > +#define MAXGENRELEN 32 > + > +#define NF_OSF_GENRE (1 << 0) > +#define NF_OSF_TTL (1 << 1) > +#define NF_OSF_LOG (1 << 2) > +#define NF_OSF_INVERT (1 << 3) > + > +#define NF_OSF_LOGLEVEL_ALL 0 /* log all matched fingerprints */ > +#define NF_OSF_LOGLEVEL_FIRST 1 /* log only the first matced fingerprint */ > +#define NF_OSF_LOGLEVEL_ALL_KNOWN 2 /* do not log unknown packets */ > + > +#define NF_OSF_TTL_TRUE 0 /* True ip and fingerprint TTL comparison */ > + > +/* Check if ip TTL is less than fingerprint one */ > +#define NF_OSF_TTL_LESS 1 > + > +/* Do not compare ip and fingerprint TTL at all */ > +#define NF_OSF_TTL_NOCHECK 2 > + > +#define NF_OSF_FLAGMASK (NF_OSF_GENRE | NF_OSF_TTL | \ > + NF_OSF_LOG | NF_OSF_INVERT) > +/* Wildcard MSS (kind of). > + * It is used to implement a state machine for the different wildcard values > + * of the MSS and window sizes. > + */ > +struct nf_osf_wc { > + __u32 wc; > + __u32 val; > +}; > + > +/* This struct represents IANA options > + * http://www.iana.org/assignments/tcp-parameters > + */ > +struct nf_osf_opt { > + __u16 kind, length; > + struct nf_osf_wc wc; > +}; > + > +struct nf_osf_info { > + char genre[MAXGENRELEN]; > + __u32 len; > + __u32 flags; > + __u32 loglevel; > + __u32 ttl; > +}; > + > +struct nf_osf_user_finger { > + struct nf_osf_wc wss; > + > + __u8 ttl, df; > + __u16 ss, mss; > + __u16 opt_num; > + > + char genre[MAXGENRELEN]; > + char version[MAXGENRELEN]; > + char subtype[MAXGENRELEN]; > + > + /* MAX_IPOPTLEN is maximum if all options are NOPs or EOLs */ > + struct nf_osf_opt opt[MAX_IPOPTLEN]; > +}; > + > +struct nf_osf_nlmsg { > + struct nf_osf_user_finger f; > + struct iphdr ip; > + struct tcphdr tcp; > +}; > + > +/* Defines for IANA option kinds */ > +enum iana_options { > + OSFOPT_EOL = 0, /* End of options */ > + OSFOPT_NOP, /* NOP */ > + OSFOPT_MSS, /* Maximum segment size */ > + OSFOPT_WSO, /* Window scale option */ > + OSFOPT_SACKP, /* SACK permitted */ > + OSFOPT_SACK, /* SACK */ > + OSFOPT_ECHO, > + OSFOPT_ECHOREPLY, > + OSFOPT_TS, /* Timestamp option */ > + OSFOPT_POCP, /* Partial Order Connection Permitted */ > + OSFOPT_POSP, /* Partial Order Service Profile */ > + > + /* Others are not used in the current OSF */ > + OSFOPT_EMPTY = 255, > +}; > + > +/* > + * Initial window size option state machine: multiple of mss, mtu or > + * plain numeric value. Can also be made as plain numeric value which > + * is not a multiple of specified value. > + */ > +enum nf_osf_window_size_options { > + OSF_WSS_PLAIN = 0, > + OSF_WSS_MSS, > + OSF_WSS_MTU, > + OSF_WSS_MODULO, > + OSF_WSS_MAX, > +}; > + > +enum nf_osf_attr_type { > + OSF_ATTR_UNSPEC, > + OSF_ATTR_FINGER, > + OSF_ATTR_MAX, > +}; > + > +/* > + * Add/remove fingerprint from the kernel. > + */ > +enum nf_osf_msg_types { > + OSF_MSG_ADD, > + OSF_MSG_REMOVE, > + OSF_MSG_MAX, > +}; > + > +#endif /* _NF_OSF_H */ > diff --git a/include/nfnl_osf.h b/include/nfnl_osf.h > new file mode 100644 > index 0000000..d9287e9 > --- /dev/null > +++ b/include/nfnl_osf.h > @@ -0,0 +1,6 @@ > +#ifndef _NFNL_OSF_H > +#define _NFNL_OSF_H > + > +int nfnl_osf_load_fingerprints(struct netlink_ctx *ctx, int del); > + > +#endif /* _NFNL_OSF_H */ > diff --git a/include/osf.h b/include/osf.h > index 715b04e..0a35b07 100644 > --- a/include/osf.h > +++ b/include/osf.h > @@ -1,6 +1,8 @@ > #ifndef NFTABLES_OSF_H > #define NFTABLES_OSF_H > > +bool osf_init; I think you can probably place osf_init in struct netlink_ctx? > struct expr *osf_expr_alloc(const struct location *loc); > > #endif /* NFTABLES_OSF_H */ > diff --git a/src/Makefile.am b/src/Makefile.am > index ed3640e..e569029 100644 > --- a/src/Makefile.am > +++ b/src/Makefile.am > @@ -57,6 +57,7 @@ libnftables_la_SOURCES = \ > services.c \ > mergesort.c \ > osf.c \ > + nfnl_osf.c \ > tcpopt.c \ > socket.c \ > libnftables.c > diff --git a/src/nfnl_osf.c b/src/nfnl_osf.c > new file mode 100644 > index 0000000..07bf682 > --- /dev/null > +++ b/src/nfnl_osf.c > @@ -0,0 +1,449 @@ > +/* > + * Copyright (c) 2005 Evgeniy Polyakov <johnpol@2ka.mxt.ru> > + * > + * > + * 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. > + */ > + > +#include <sys/time.h> > + > +#include <ctype.h> > +#include <errno.h> > +#include <stdlib.h> > +#include <string.h> > +#include <time.h> > + > +#include <netinet/ip.h> > +#include <netinet/tcp.h> > + > +#include <linux/unistd.h> > + > +#include <libmnl/libmnl.h> > + > +#include <linux/netfilter/nfnetlink.h> > +#include <linux/netfilter/nfnetlink_osf.h> > +#include <mnl.h> > +#include <nfnl_osf.h> > + > +#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 void uloga(const char *f, struct netlink_ctx *ctx, ...) > +{ > + if (!(ctx->debug_mask & NFT_DEBUG_NETLINK)) > + return; > + > + nft_print(ctx->octx, "%s", f); > +} I think you can use uloga() all the time, so you can remove ulog() function. > +static void ulog(const char *f, struct netlink_ctx *ctx, ...) > +{ > + char str[64]; > + struct tm tm; > + struct timeval tv; > + > + gettimeofday(&tv, NULL); > + localtime_r((time_t *)&tv.tv_sec, &tm); > + strftime(str, sizeof(str), "%F %R:%S", &tm); > + > + if (!(ctx->debug_mask & NFT_DEBUG_NETLINK)) > + return; > + > + nft_print(ctx->octx, "%s.%lu %ld %s", str, tv.tv_usec, > + syscall(__NR_gettid), f); > +} > + > +#define ulog_err(f, ctx, a...) uloga(f ": %s [%d].\n", ctx, ##a, strerror(errno), errno) And this macro too. Other than that, this looks good to me, thanks.
On 08/11/2018 12:03 PM, Pablo Neira Ayuso wrote: >> +#endif /* _NF_OSF_H */ >> diff --git a/include/nfnl_osf.h b/include/nfnl_osf.h >> new file mode 100644 >> index 0000000..d9287e9 >> --- /dev/null >> +++ b/include/nfnl_osf.h >> @@ -0,0 +1,6 @@ >> +#ifndef _NFNL_OSF_H >> +#define _NFNL_OSF_H >> + >> +int nfnl_osf_load_fingerprints(struct netlink_ctx *ctx, int del); >> + >> +#endif /* _NFNL_OSF_H */ >> diff --git a/include/osf.h b/include/osf.h >> index 715b04e..0a35b07 100644 >> --- a/include/osf.h >> +++ b/include/osf.h >> @@ -1,6 +1,8 @@ >> #ifndef NFTABLES_OSF_H >> #define NFTABLES_OSF_H >> >> +bool osf_init; > > I think you can probably place osf_init in struct netlink_ctx? > If we place osf_init in struct netlink_ctx we will need to modify osf_expr_alloc() and I am not sure if we can get access to netlink_ctx from netlink_parse_osf() in netlink_delinearize.c. Also we will need access to netlink_ctx from parser_bison.y. So I propose to add osf_init in nfnl_osf.h in order to have only one extra include in rule.c. Thanks. >> struct expr *osf_expr_alloc(const struct location *loc); >> >> #endif /* NFTABLES_OSF_H */ >> diff --git a/src/Makefile.am b/src/Makefile.am >> index ed3640e..e569029 100644 >> --- a/src/Makefile.am >> +++ b/src/Makefile.am >> @@ -57,6 +57,7 @@ libnftables_la_SOURCES = \ >> services.c \ >> mergesort.c \ >> osf.c \ >> + nfnl_osf.c \ >> tcpopt.c \ >> socket.c \ >> libnftables.c >> diff --git a/src/nfnl_osf.c b/src/nfnl_osf.c >> new file mode 100644 >> index 0000000..07bf682 >> --- /dev/null >> +++ b/src/nfnl_osf.c >> @@ -0,0 +1,449 @@ >> +/* >> + * Copyright (c) 2005 Evgeniy Polyakov <johnpol@2ka.mxt.ru> >> + * >> + * >> + * 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. >> + */ >> + >> +#include <sys/time.h> >> + >> +#include <ctype.h> >> +#include <errno.h> >> +#include <stdlib.h> >> +#include <string.h> >> +#include <time.h> >> + >> +#include <netinet/ip.h> >> +#include <netinet/tcp.h> >> + >> +#include <linux/unistd.h> >> + >> +#include <libmnl/libmnl.h> >> + >> +#include <linux/netfilter/nfnetlink.h> >> +#include <linux/netfilter/nfnetlink_osf.h> >> +#include <mnl.h> >> +#include <nfnl_osf.h> >> + >> +#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 void uloga(const char *f, struct netlink_ctx *ctx, ...) >> +{ >> + if (!(ctx->debug_mask & NFT_DEBUG_NETLINK)) >> + return; >> + >> + nft_print(ctx->octx, "%s", f); >> +} > > I think you can use uloga() all the time, so you can remove ulog() > function. > I agree. Changes done. >> +static void ulog(const char *f, struct netlink_ctx *ctx, ...) >> +{ >> + char str[64]; >> + struct tm tm; >> + struct timeval tv; >> + >> + gettimeofday(&tv, NULL); >> + localtime_r((time_t *)&tv.tv_sec, &tm); >> + strftime(str, sizeof(str), "%F %R:%S", &tm); >> + >> + if (!(ctx->debug_mask & NFT_DEBUG_NETLINK)) >> + return; >> + >> + nft_print(ctx->octx, "%s.%lu %ld %s", str, tv.tv_usec, >> + syscall(__NR_gettid), f); >> +} >> + >> +#define ulog_err(f, ctx, a...) uloga(f ": %s [%d].\n", ctx, ##a, strerror(errno), errno) > > And this macro too. > > Other than that, this looks good to me, thanks. >
On Sat, Aug 11, 2018 at 05:17:29PM +0200, Fernando Fernandez Mancera wrote: [...] > If we place osf_init in struct netlink_ctx we will need to modify > osf_expr_alloc() and I am not sure if we can get access to netlink_ctx from > netlink_parse_osf() in netlink_delinearize.c. Also we will need access to > netlink_ctx from parser_bison.y. > > So I propose to add osf_init in nfnl_osf.h in order to have only one extra > include in rule.c. Thanks. OK. Would you send a v2? Thanks.
diff --git a/include/linux/netfilter/nfnetlink_osf.h b/include/linux/netfilter/nfnetlink_osf.h new file mode 100644 index 0000000..15a39d2 --- /dev/null +++ b/include/linux/netfilter/nfnetlink_osf.h @@ -0,0 +1,119 @@ +#ifndef _NF_OSF_H +#define _NF_OSF_H + +#include <linux/types.h> + +#define MAXGENRELEN 32 + +#define NF_OSF_GENRE (1 << 0) +#define NF_OSF_TTL (1 << 1) +#define NF_OSF_LOG (1 << 2) +#define NF_OSF_INVERT (1 << 3) + +#define NF_OSF_LOGLEVEL_ALL 0 /* log all matched fingerprints */ +#define NF_OSF_LOGLEVEL_FIRST 1 /* log only the first matced fingerprint */ +#define NF_OSF_LOGLEVEL_ALL_KNOWN 2 /* do not log unknown packets */ + +#define NF_OSF_TTL_TRUE 0 /* True ip and fingerprint TTL comparison */ + +/* Check if ip TTL is less than fingerprint one */ +#define NF_OSF_TTL_LESS 1 + +/* Do not compare ip and fingerprint TTL at all */ +#define NF_OSF_TTL_NOCHECK 2 + +#define NF_OSF_FLAGMASK (NF_OSF_GENRE | NF_OSF_TTL | \ + NF_OSF_LOG | NF_OSF_INVERT) +/* Wildcard MSS (kind of). + * It is used to implement a state machine for the different wildcard values + * of the MSS and window sizes. + */ +struct nf_osf_wc { + __u32 wc; + __u32 val; +}; + +/* This struct represents IANA options + * http://www.iana.org/assignments/tcp-parameters + */ +struct nf_osf_opt { + __u16 kind, length; + struct nf_osf_wc wc; +}; + +struct nf_osf_info { + char genre[MAXGENRELEN]; + __u32 len; + __u32 flags; + __u32 loglevel; + __u32 ttl; +}; + +struct nf_osf_user_finger { + struct nf_osf_wc wss; + + __u8 ttl, df; + __u16 ss, mss; + __u16 opt_num; + + char genre[MAXGENRELEN]; + char version[MAXGENRELEN]; + char subtype[MAXGENRELEN]; + + /* MAX_IPOPTLEN is maximum if all options are NOPs or EOLs */ + struct nf_osf_opt opt[MAX_IPOPTLEN]; +}; + +struct nf_osf_nlmsg { + struct nf_osf_user_finger f; + struct iphdr ip; + struct tcphdr tcp; +}; + +/* Defines for IANA option kinds */ +enum iana_options { + OSFOPT_EOL = 0, /* End of options */ + OSFOPT_NOP, /* NOP */ + OSFOPT_MSS, /* Maximum segment size */ + OSFOPT_WSO, /* Window scale option */ + OSFOPT_SACKP, /* SACK permitted */ + OSFOPT_SACK, /* SACK */ + OSFOPT_ECHO, + OSFOPT_ECHOREPLY, + OSFOPT_TS, /* Timestamp option */ + OSFOPT_POCP, /* Partial Order Connection Permitted */ + OSFOPT_POSP, /* Partial Order Service Profile */ + + /* Others are not used in the current OSF */ + OSFOPT_EMPTY = 255, +}; + +/* + * Initial window size option state machine: multiple of mss, mtu or + * plain numeric value. Can also be made as plain numeric value which + * is not a multiple of specified value. + */ +enum nf_osf_window_size_options { + OSF_WSS_PLAIN = 0, + OSF_WSS_MSS, + OSF_WSS_MTU, + OSF_WSS_MODULO, + OSF_WSS_MAX, +}; + +enum nf_osf_attr_type { + OSF_ATTR_UNSPEC, + OSF_ATTR_FINGER, + OSF_ATTR_MAX, +}; + +/* + * Add/remove fingerprint from the kernel. + */ +enum nf_osf_msg_types { + OSF_MSG_ADD, + OSF_MSG_REMOVE, + OSF_MSG_MAX, +}; + +#endif /* _NF_OSF_H */ diff --git a/include/nfnl_osf.h b/include/nfnl_osf.h new file mode 100644 index 0000000..d9287e9 --- /dev/null +++ b/include/nfnl_osf.h @@ -0,0 +1,6 @@ +#ifndef _NFNL_OSF_H +#define _NFNL_OSF_H + +int nfnl_osf_load_fingerprints(struct netlink_ctx *ctx, int del); + +#endif /* _NFNL_OSF_H */ diff --git a/include/osf.h b/include/osf.h index 715b04e..0a35b07 100644 --- a/include/osf.h +++ b/include/osf.h @@ -1,6 +1,8 @@ #ifndef NFTABLES_OSF_H #define NFTABLES_OSF_H +bool osf_init; + struct expr *osf_expr_alloc(const struct location *loc); #endif /* NFTABLES_OSF_H */ diff --git a/src/Makefile.am b/src/Makefile.am index ed3640e..e569029 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -57,6 +57,7 @@ libnftables_la_SOURCES = \ services.c \ mergesort.c \ osf.c \ + nfnl_osf.c \ tcpopt.c \ socket.c \ libnftables.c diff --git a/src/nfnl_osf.c b/src/nfnl_osf.c new file mode 100644 index 0000000..07bf682 --- /dev/null +++ b/src/nfnl_osf.c @@ -0,0 +1,449 @@ +/* + * Copyright (c) 2005 Evgeniy Polyakov <johnpol@2ka.mxt.ru> + * + * + * 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. + */ + +#include <sys/time.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <netinet/ip.h> +#include <netinet/tcp.h> + +#include <linux/unistd.h> + +#include <libmnl/libmnl.h> + +#include <linux/netfilter/nfnetlink.h> +#include <linux/netfilter/nfnetlink_osf.h> +#include <mnl.h> +#include <nfnl_osf.h> + +#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 void uloga(const char *f, struct netlink_ctx *ctx, ...) +{ + if (!(ctx->debug_mask & NFT_DEBUG_NETLINK)) + return; + + nft_print(ctx->octx, "%s", f); +} + +static void ulog(const char *f, struct netlink_ctx *ctx, ...) +{ + char str[64]; + struct tm tm; + struct timeval tv; + + gettimeofday(&tv, NULL); + localtime_r((time_t *)&tv.tv_sec, &tm); + strftime(str, sizeof(str), "%F %R:%S", &tm); + + if (!(ctx->debug_mask & NFT_DEBUG_NETLINK)) + return; + + nft_print(ctx->octx, "%s.%lu %ld %s", str, tv.tv_usec, + syscall(__NR_gettid), f); +} + +#define ulog_err(f, ctx, a...) uloga(f ": %s [%d].\n", ctx, ##a, strerror(errno), errno) + +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 mnl_socket *nl, + 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)); + + ulog("Loading '%s'.\n", ctx, buffer); + + for (i = 0; i < len && buffer[i] != '\0'; ++i) { + if (buffer[i] == ':') + cnt++; + } + + if (cnt != 8) { + ulog("Wrong input line '%s': cnt: %d, must be 8, i: %d, must be %d.\n", ctx, 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 = nftnl_nlmsg_build_hdr(buf, (NFNL_SUBSYS_OSF << 8) | + OSF_MSG_REMOVE, AF_UNSPEC, + NLM_F_REQUEST | NLM_F_ACK, + 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 = nftnl_nlmsg_build_hdr(buf, (NFNL_SUBSYS_OSF << 8) | + OSF_MSG_ADD, AF_UNSPEC, + NLM_F_REQUEST | NLM_F_CREATE | + NLM_F_ACK, 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); +} + +static int osf_load_entries(const char *path, int del, struct mnl_socket *nl, + struct netlink_ctx *ctx) +{ + FILE *inf; + int err = 0; + char buf[1024]; + + inf = fopen(path, "r"); + if (!inf) { + ulog_err("Failed to open file '%s'", ctx, path); + 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, nl, ctx); + if (err) + break; + + memset(buf, 0, sizeof(buf)); + } + + fclose(inf); + return err; +} + +int nfnl_osf_load_fingerprints(struct netlink_ctx *ctx, int del) +{ + int err; + struct mnl_socket *nl; + const char *fingerprints = "files/osf/pf.os"; + + if (!fingerprints) { + err = -ENOENT; + goto err_out_exit; + } + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + err = -EINVAL; + ulog_err("Failed to open mnl socket", ctx); + goto err_out_exit; + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + err = -EINVAL; + ulog_err("Failed to bind mnl socket", ctx); + goto err_out_exit; + } + +#ifndef NFNL_SUBSYS_OSF +#define NFNL_SUBSYS_OSF 5 +#endif + + err = osf_load_entries(fingerprints, del, nl, ctx); + if (err < 0) + goto err_out_close; + + return 0; + +err_out_close: + mnl_socket_close(nl); +err_out_exit: + return err; +} diff --git a/src/osf.c b/src/osf.c index 131d54e..210bfbe 100644 --- a/src/osf.c +++ b/src/osf.c @@ -3,6 +3,7 @@ #include <utils.h> #include <string.h> #include <osf.h> +#include <nfnl_osf.h> static void osf_expr_print(const struct expr *expr, struct output_ctx *octx) { @@ -26,6 +27,7 @@ struct expr *osf_expr_alloc(const struct location *loc) const struct datatype *type = &string_type; struct expr *expr; + osf_init = true; expr = expr_alloc(loc, &osf_expr_ops, type, BYTEORDER_HOST_ENDIAN, len); diff --git a/src/rule.c b/src/rule.c index 7a7ac73..dc1ce8b 100644 --- a/src/rule.c +++ b/src/rule.c @@ -22,6 +22,8 @@ #include <netdb.h> #include <netlink.h> #include <json.h> +#include <nfnl_osf.h> +#include <osf.h> #include <libnftnl/common.h> #include <libnftnl/ruleset.h> @@ -2135,6 +2137,9 @@ int do_command(struct netlink_ctx *ctx, struct cmd *cmd) default: BUG("invalid command object type %u\n", cmd->obj); } + + if (osf_init) + nfnl_osf_load_fingerprints(ctx, 0); } static int payload_match_stmt_cmp(const void *p1, const void *p2)
Import iptables/utils/nfnl_osf.c into nftables tree with some changes in order to load OS fingerprints automatically from pf.os file. Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net> --- include/linux/netfilter/nfnetlink_osf.h | 119 +++++++ include/nfnl_osf.h | 6 + include/osf.h | 2 + src/Makefile.am | 1 + src/nfnl_osf.c | 449 ++++++++++++++++++++++++ src/osf.c | 2 + src/rule.c | 5 + 7 files changed, 584 insertions(+) create mode 100644 include/linux/netfilter/nfnetlink_osf.h create mode 100644 include/nfnl_osf.h create mode 100644 src/nfnl_osf.c