On Mon, Aug 13, 2018 at 06:57:08PM +0200, Fernando Fernandez Mancera wrote:
[...]
> diff --git a/include/nfnl_osf.h b/include/nfnl_osf.h
> new file mode 100644
> index 0000000..b676045
> --- /dev/null
> +++ b/include/nfnl_osf.h
No need for a new nfnl_osf.h file, please use the existing
include/osf.h.
> @@ -0,0 +1,10 @@
> +#ifndef _NFNL_OSF_H
> +#define _NFNL_OSF_H
> +
> +#define OS_SIGNATURES DEFAULT_INCLUDE_PATH "/pf.os"
> +
> +bool osf_init;
This needs to be:
extern bool osf_init;
Then, define 'bool osf_init' from the nfnl_osf.c file.
Make sure you update include/Makefile.am so `make distcheck' doesn't
break.
> +
> +int nfnl_osf_load_fingerprints(struct netlink_ctx *ctx, int del);
> +
> +#endif /* _NFNL_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..53e4ec6
> --- /dev/null
> +++ b/src/nfnl_osf.c
> @@ -0,0 +1,424 @@
> +/*
> + * Copyright (c) 2005 Evgeniy Polyakov <[email protected]>
> + *
> + *
> + * 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 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));
> +
> + uloga("Loading '%s'.\n", ctx, buffer);
> +
> + for (i = 0; i < len && buffer[i] != '\0'; ++i) {
> + if (buffer[i] == ':')
> + cnt++;
> + }
> +
> + if (cnt != 8) {
> + uloga("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(int del, struct mnl_socket *nl,
> + struct netlink_ctx *ctx)
> +{
> + FILE *inf;
> + int err = 0;
> + char buf[1024];
> +
> + inf = fopen(OS_SIGNATURES, "r");
> + if (!inf) {
> + uloga("Failed to open file '%s'", ctx, 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, 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;
> +
> + nl = mnl_socket_open(NETLINK_NETFILTER);
> + if (nl == NULL) {
> + err = -EINVAL;
> + uloga("Failed to open mnl socket", ctx);
> + goto err_out_exit;
> + }
> +
> + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
> + err = -EINVAL;
> + uloga("Failed to bind mnl socket", ctx);
> + goto err_out_exit;
> + }
> +
> +#ifndef NFNL_SUBSYS_OSF
> +#define NFNL_SUBSYS_OSF 5
> +#endif
Remove this #ifdef, not needed. NFNL_SUBSYS_OSF is already defined in
nfnetlink_osf.h
> + err = osf_load_entries(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..7421ffc 100644
> --- a/src/rule.c
> +++ b/src/rule.c
> @@ -22,6 +22,7 @@
> #include <netdb.h>
> #include <netlink.h>
> #include <json.h>
> +#include <nfnl_osf.h>
>
> #include <libnftnl/common.h>
> #include <libnftnl/ruleset.h>
> @@ -2135,6 +2136,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);
You have to update do_command() to replace all:
case blah:
return ...;
to
case blah:
err = ...;
break;
Otherwise this code is never exercised.
When testing this, make sure nfnl_osf_load_fingerprints() is called.
You can use:
nft --debug=netlink add rule x y osf name "linux"
to see if you trigger uloga() calls for debugging so you make sure you
properly test your patches.
Thanks.