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 <ffmanc...@riseup.net>
---
 include/linux/netfilter/Makefile.am     |   1 +
 include/linux/netfilter/nfnetlink_osf.h | 119 +++++++
 include/osf.h                           |   5 +
 src/Makefile.am                         |   1 +
 src/nfnl_osf.c                          | 401 ++++++++++++++++++++++++
 src/osf.c                               |   1 +
 src/rule.c                              |  29 +-
 7 files changed, 549 insertions(+), 8 deletions(-)
 create mode 100644 include/linux/netfilter/nfnetlink_osf.h
 create mode 100644 src/nfnl_osf.c

diff --git a/include/linux/netfilter/Makefile.am 
b/include/linux/netfilter/Makefile.am
index f6a8aa2..2c1de18 100644
--- a/include/linux/netfilter/Makefile.am
+++ b/include/linux/netfilter/Makefile.am
@@ -3,4 +3,5 @@ noinst_HEADERS =        nf_conntrack_common.h           \
                        nf_log.h                        \
                        nf_nat.h                        \
                        nf_tables.h                     \
+                       nfnetlink_osf.h                 \
                        nfnetlink.h
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/osf.h b/include/osf.h
index 715b04e..7f52169 100644
--- a/include/osf.h
+++ b/include/osf.h
@@ -1,6 +1,11 @@
 #ifndef NFTABLES_OSF_H
 #define NFTABLES_OSF_H
 
+#define OS_SIGNATURES DEFAULT_INCLUDE_PATH "/osf/pf.os"
+
+extern bool osf_init;
+
+extern int nfnl_osf_load_fingerprints(struct netlink_ctx *ctx, int del);
 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..e8183e3
--- /dev/null
+++ b/src/nfnl_osf.c
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 2005 Evgeniy Polyakov <john...@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 <osf.h>
+
+#define OPTDEL                 ','
+#define OSFPDEL                ':'
+#define MAXOPTSTRLEN           128
+
+bool osf_init;
+
+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_NETLINK)
+               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_NETLINK)
+                       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);
+}
+
+static int osf_load_entries(int del, struct netlink_ctx *ctx)
+{
+       FILE *inf;
+       int err = 0;
+       char buf[1024];
+
+       inf = fopen(OS_SIGNATURES, "r");
+       if (!inf) {
+               if (ctx->debug_mask & NFT_DEBUG_NETLINK)
+                       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;
+}
+
+int nfnl_osf_load_fingerprints(struct netlink_ctx *ctx, int del)
+{
+       uint32_t err;
+
+       err = osf_load_entries(del, ctx);
+       if (err < 0)
+               goto err_out_exit;
+
+       return 0;
+
+err_out_exit:
+       return err;
+}
diff --git a/src/osf.c b/src/osf.c
index 131d54e..b8457cc 100644
--- a/src/osf.c
+++ b/src/osf.c
@@ -26,6 +26,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 570d667..cbed5a9 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -1323,6 +1323,7 @@ static int do_add_set(struct netlink_ctx *ctx, const 
struct cmd *cmd,
 static int do_command_add(struct netlink_ctx *ctx, struct cmd *cmd, bool excl)
 {
        uint32_t flags = excl ? NLM_F_EXCL : 0;
+       uint32_t err;
 
        if (ctx->octx->echo) {
                int ret;
@@ -1337,26 +1338,37 @@ static int do_command_add(struct netlink_ctx *ctx, 
struct cmd *cmd, bool excl)
 
        switch (cmd->obj) {
        case CMD_OBJ_TABLE:
-               return netlink_add_table_batch(ctx, cmd, flags);
+               err = netlink_add_table_batch(ctx, cmd, flags);
+               break;
        case CMD_OBJ_CHAIN:
-               return netlink_add_chain_batch(ctx, cmd, flags);
+               err = netlink_add_chain_batch(ctx, cmd, flags);
+               break;
        case CMD_OBJ_RULE:
-               return netlink_add_rule_batch(ctx, cmd, flags | NLM_F_APPEND);
+               err = netlink_add_rule_batch(ctx, cmd, flags | NLM_F_APPEND);
+               break;
        case CMD_OBJ_SET:
-               return do_add_set(ctx, cmd, flags);
+               err = do_add_set(ctx, cmd, flags);
+               break;
        case CMD_OBJ_SETELEM:
-               return do_add_setelems(ctx, cmd, flags);
+               err = do_add_setelems(ctx, cmd, flags);
+               break;
        case CMD_OBJ_COUNTER:
        case CMD_OBJ_QUOTA:
        case CMD_OBJ_CT_HELPER:
        case CMD_OBJ_LIMIT:
-               return netlink_add_obj(ctx, cmd, flags);
+               err = netlink_add_obj(ctx, cmd, flags);
+               break;
        case CMD_OBJ_FLOWTABLE:
-               return netlink_add_flowtable(ctx, cmd, flags);
+               err = netlink_add_flowtable(ctx, cmd, flags);
+               break;
        default:
                BUG("invalid command object type %u\n", cmd->obj);
        }
-       return 0;
+
+       if (osf_init)
+               err = nfnl_osf_load_fingerprints(ctx, 0);
+
+       return err;
 }
 
 static int do_command_replace(struct netlink_ctx *ctx, struct cmd *cmd)
@@ -2270,6 +2282,7 @@ struct cmd *cmd_alloc_obj_ct(enum cmd_ops op, int type, 
const struct handle *h,
 
 int do_command(struct netlink_ctx *ctx, struct cmd *cmd)
 {
+
        switch (cmd->op) {
        case CMD_ADD:
                return do_command_add(ctx, cmd, false);
-- 
2.18.0

Reply via email to