summaryrefslogtreecommitdiff
path: root/lib/route/cls/ematch/nbyte.c
blob: 735dfc869fcdfc0317d1cb01e6d55b518384cc46 (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
134
/* SPDX-License-Identifier: LGPL-2.1-only */
/*
 * Copyright (c) 2010-2013 Thomas Graf <tgraf@suug.ch>
 */

/**
 * @ingroup ematch
 * @defgroup em_nbyte N-Byte Comparison
 *
 * @{
 */

#include <netlink-private/netlink.h>
#include <netlink-private/tc.h>
#include <netlink/netlink.h>
#include <netlink/route/cls/ematch.h>
#include <netlink/route/cls/ematch/nbyte.h>
#include <linux/tc_ematch/tc_em_nbyte.h>

struct nbyte_data
{
	struct tcf_em_nbyte	cfg;
	uint8_t *		pattern;
};

void rtnl_ematch_nbyte_set_offset(struct rtnl_ematch *e, uint8_t layer,
				  uint16_t offset)
{
	struct nbyte_data *n = rtnl_ematch_data(e);
	n->cfg.off = offset;
	n->cfg.layer = layer;
}

uint16_t rtnl_ematch_nbyte_get_offset(struct rtnl_ematch *e)
{
	return ((struct nbyte_data *) rtnl_ematch_data(e))->cfg.off;
}

uint8_t rtnl_ematch_nbyte_get_layer(struct rtnl_ematch *e)
{
	return ((struct nbyte_data *) rtnl_ematch_data(e))->cfg.layer;
}

void rtnl_ematch_nbyte_set_pattern(struct rtnl_ematch *e,
				   uint8_t *pattern, size_t len)
{
	struct nbyte_data *n = rtnl_ematch_data(e);

	if (n->pattern)
		free(n->pattern);

	n->pattern = pattern;
	n->cfg.len = len;
}

uint8_t *rtnl_ematch_nbyte_get_pattern(struct rtnl_ematch *e)
{
	return ((struct nbyte_data *) rtnl_ematch_data(e))->pattern;
}

size_t rtnl_ematch_nbyte_get_len(struct rtnl_ematch *e)
{
	return ((struct nbyte_data *) rtnl_ematch_data(e))->cfg.len;
}

static const char *layer_txt(struct tcf_em_nbyte *nbyte)
{
	switch (nbyte->layer) {
	case TCF_LAYER_LINK:
		return "link";
	case TCF_LAYER_NETWORK:
		return "net";
	case TCF_LAYER_TRANSPORT:
		return "trans";
	default:
		return "?";
	}
}

static int nbyte_parse(struct rtnl_ematch *e, void *data, size_t len)
{
	struct nbyte_data *n = rtnl_ematch_data(e);
	size_t hdrlen = sizeof(struct tcf_em_nbyte);
	size_t plen = len - hdrlen;

	memcpy(&n->cfg, data, hdrlen);
	if (plen > 0) {
		if (!(n->pattern = calloc(1, plen)))
			return -NLE_NOMEM;

		memcpy(n->pattern, (char *) data + hdrlen, plen);
	}

	return 0;
}

static void nbyte_dump(struct rtnl_ematch *e, struct nl_dump_params *p)
{
	struct nbyte_data *n = rtnl_ematch_data(e);
	int i;

	nl_dump(p, "pattern(%u:[", n->cfg.len);

	for (i = 0; i < n->cfg.len; i++) {
		nl_dump(p, "%02x", n->pattern[i]);
		if (i+1 < n->cfg.len)
			nl_dump(p, " ");
	}

	nl_dump(p, "] at %s+%u)", layer_txt(&n->cfg), n->cfg.off);
}

static void nbyte_free(struct rtnl_ematch *e)
{
	struct nbyte_data *n = rtnl_ematch_data(e);
	free(n->pattern);
}

static struct rtnl_ematch_ops nbyte_ops = {
	.eo_kind	= TCF_EM_NBYTE,
	.eo_name	= "nbyte",
	.eo_minlen	= sizeof(struct tcf_em_nbyte),
	.eo_datalen	= sizeof(struct nbyte_data),
	.eo_parse	= nbyte_parse,
	.eo_dump	= nbyte_dump,
	.eo_free	= nbyte_free,
};

static void __init nbyte_init(void)
{
	rtnl_ematch_register(&nbyte_ops);
}

/** @} */