summaryrefslogtreecommitdiffstats
path: root/iptables/nft-ruleparse-ipv4.c
blob: fe65b33cf847b0c942b446b002eafc73b3ae6f98 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/*
 * (C) 2012-2014 by Pablo Neira Ayuso <pablo@netfilter.org>
 * (C) 2013 by Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
 *
 * 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 code has been sponsored by Sophos Astaro <http://www.sophos.com>
 */

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <net/if.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>

#include <libnftnl/rule.h>
#include <libnftnl/expr.h>

#include "nft-shared.h"
#include "nft-ruleparse.h"
#include "xshared.h"

static void nft_ipv4_parse_meta(struct nft_xt_ctx *ctx,
				const struct nft_xt_ctx_reg *reg,
				struct nftnl_expr *e,
				struct iptables_command_state *cs)
{
	switch (reg->meta_dreg.key) {
	case NFT_META_L4PROTO:
		cs->fw.ip.proto = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
		if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
			cs->fw.ip.invflags |= XT_INV_PROTO;
		return;
	default:
		break;
	}

	if (parse_meta(ctx, e, reg->meta_dreg.key, cs->fw.ip.iniface,
		       cs->fw.ip.outiface, &cs->fw.ip.invflags) == 0)
		return;

	ctx->errmsg = "unknown ipv4 meta key";
}

static void parse_mask_ipv4(const struct nft_xt_ctx_reg *sreg, struct in_addr *mask)
{
	mask->s_addr = sreg->bitwise.mask[0];
}

static bool get_frag(const struct nft_xt_ctx_reg *reg, struct nftnl_expr *e)
{
	uint8_t op;

	/* we assume correct mask and xor */
	if (!reg->bitwise.set)
		return false;

	/* we assume correct data */
	op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
	if (op == NFT_CMP_EQ)
		return true;

	return false;
}

static void nft_ipv4_parse_payload(struct nft_xt_ctx *ctx,
				   const struct nft_xt_ctx_reg *sreg,
				   struct nftnl_expr *e,
				   struct iptables_command_state *cs)
{
	struct in_addr addr;
	uint8_t proto;
	bool inv;

	switch (sreg->payload.offset) {
	case offsetof(struct iphdr, saddr):
		get_cmp_data(e, &addr, sizeof(addr), &inv);
		cs->fw.ip.src.s_addr = addr.s_addr;
		if (sreg->bitwise.set) {
			parse_mask_ipv4(sreg, &cs->fw.ip.smsk);
		} else {
			memset(&cs->fw.ip.smsk, 0xff,
			       min(sreg->payload.len, sizeof(struct in_addr)));
		}

		if (inv)
			cs->fw.ip.invflags |= IPT_INV_SRCIP;
		break;
	case offsetof(struct iphdr, daddr):
		get_cmp_data(e, &addr, sizeof(addr), &inv);
		cs->fw.ip.dst.s_addr = addr.s_addr;
		if (sreg->bitwise.set)
			parse_mask_ipv4(sreg, &cs->fw.ip.dmsk);
		else
			memset(&cs->fw.ip.dmsk, 0xff,
			       min(sreg->payload.len, sizeof(struct in_addr)));

		if (inv)
			cs->fw.ip.invflags |= IPT_INV_DSTIP;
		break;
	case offsetof(struct iphdr, protocol):
		get_cmp_data(e, &proto, sizeof(proto), &inv);
		cs->fw.ip.proto = proto;
		if (inv)
			cs->fw.ip.invflags |= IPT_INV_PROTO;
		break;
	case offsetof(struct iphdr, frag_off):
		cs->fw.ip.flags |= IPT_F_FRAG;
		inv = get_frag(sreg, e);
		if (inv)
			cs->fw.ip.invflags |= IPT_INV_FRAG;
		break;
	case offsetof(struct iphdr, ttl):
		if (nft_parse_hl(ctx, e, cs) < 0)
			ctx->errmsg = "invalid ttl field match";
		break;
	default:
		DEBUGP("unknown payload offset %d\n", sreg->payload.offset);
		ctx->errmsg = "unknown payload offset";
		break;
	}
}

struct nft_ruleparse_ops nft_ruleparse_ops_ipv4 = {
	.meta		= nft_ipv4_parse_meta,
	.payload	= nft_ipv4_parse_payload,
};