1d359a62dSAndrey V. Elsukov /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3fe267a55SPedro F. Giffuni *
4bf909fc9SJulian Elischer * Copyright (c) 2010 Maxim Ignatenko <gelraen.ua@gmail.com>
5426b3d04SJulian Elischer * Copyright (c) 2015 Dmitry Vagin <daemon.hammer@ya.ru>
6d359a62dSAndrey V. Elsukov * All rights reserved.
7d359a62dSAndrey V. Elsukov *
8d359a62dSAndrey V. Elsukov * Redistribution and use in source and binary forms, with or without
9d359a62dSAndrey V. Elsukov * modification, are permitted provided that the following conditions
10d359a62dSAndrey V. Elsukov * are met:
11d359a62dSAndrey V. Elsukov * 1. Redistributions of source code must retain the above copyright
12d359a62dSAndrey V. Elsukov * notice, this list of conditions and the following disclaimer.
13d359a62dSAndrey V. Elsukov * 2. Redistributions in binary form must reproduce the above copyright
14d359a62dSAndrey V. Elsukov * notice, this list of conditions and the following disclaimer in the
15d359a62dSAndrey V. Elsukov * documentation and/or other materials provided with the distribution.
16d359a62dSAndrey V. Elsukov *
17d359a62dSAndrey V. Elsukov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18d359a62dSAndrey V. Elsukov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19d359a62dSAndrey V. Elsukov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20d359a62dSAndrey V. Elsukov * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21d359a62dSAndrey V. Elsukov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22d359a62dSAndrey V. Elsukov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23d359a62dSAndrey V. Elsukov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24d359a62dSAndrey V. Elsukov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25d359a62dSAndrey V. Elsukov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26d359a62dSAndrey V. Elsukov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27d359a62dSAndrey V. Elsukov * SUCH DAMAGE.
28d359a62dSAndrey V. Elsukov *
29d359a62dSAndrey V. Elsukov */
30d359a62dSAndrey V. Elsukov
31d359a62dSAndrey V. Elsukov #include <sys/param.h>
32cac2fe69SAndrey V. Elsukov #include <sys/systm.h>
33d359a62dSAndrey V. Elsukov #include <sys/kernel.h>
34cac2fe69SAndrey V. Elsukov #include <sys/endian.h>
356c0a2eb1SAndrey V. Elsukov #include <sys/malloc.h>
366c0a2eb1SAndrey V. Elsukov #include <sys/mbuf.h>
37426b3d04SJulian Elischer
38426b3d04SJulian Elischer #include <net/bpf.h>
39426b3d04SJulian Elischer #include <net/ethernet.h>
40426b3d04SJulian Elischer
41d359a62dSAndrey V. Elsukov #include <netgraph/ng_message.h>
42d359a62dSAndrey V. Elsukov #include <netgraph/ng_parse.h>
43d359a62dSAndrey V. Elsukov #include <netgraph/netgraph.h>
44d359a62dSAndrey V. Elsukov
45426b3d04SJulian Elischer #include <netgraph/ng_patch.h>
46426b3d04SJulian Elischer
47426b3d04SJulian Elischer /* private data */
48426b3d04SJulian Elischer struct ng_patch_priv {
49426b3d04SJulian Elischer hook_p in;
50426b3d04SJulian Elischer hook_p out;
51426b3d04SJulian Elischer uint8_t dlt; /* DLT_XXX from bpf.h */
52426b3d04SJulian Elischer struct ng_patch_stats stats;
53426b3d04SJulian Elischer struct ng_patch_config *conf;
54426b3d04SJulian Elischer };
55426b3d04SJulian Elischer
56426b3d04SJulian Elischer typedef struct ng_patch_priv *priv_p;
57426b3d04SJulian Elischer
58426b3d04SJulian Elischer /* Netgraph methods */
59d359a62dSAndrey V. Elsukov static ng_constructor_t ng_patch_constructor;
60d359a62dSAndrey V. Elsukov static ng_rcvmsg_t ng_patch_rcvmsg;
61d359a62dSAndrey V. Elsukov static ng_shutdown_t ng_patch_shutdown;
62d359a62dSAndrey V. Elsukov static ng_newhook_t ng_patch_newhook;
63d359a62dSAndrey V. Elsukov static ng_rcvdata_t ng_patch_rcvdata;
64d359a62dSAndrey V. Elsukov static ng_disconnect_t ng_patch_disconnect;
65426b3d04SJulian Elischer #define ERROUT(x) { error = (x); goto done; }
66426b3d04SJulian Elischer
67d359a62dSAndrey V. Elsukov static int
ng_patch_config_getlen(const struct ng_parse_type * type,const u_char * start,const u_char * buf)686c0a2eb1SAndrey V. Elsukov ng_patch_config_getlen(const struct ng_parse_type *type,
696c0a2eb1SAndrey V. Elsukov const u_char *start, const u_char *buf)
70d359a62dSAndrey V. Elsukov {
71bf909fc9SJulian Elischer const struct ng_patch_config *conf;
72d359a62dSAndrey V. Elsukov
73bf909fc9SJulian Elischer conf = (const struct ng_patch_config *)(buf -
74cac2fe69SAndrey V. Elsukov offsetof(struct ng_patch_config, ops));
75bf909fc9SJulian Elischer
76bf909fc9SJulian Elischer return (conf->count);
77d359a62dSAndrey V. Elsukov }
78d359a62dSAndrey V. Elsukov
79d359a62dSAndrey V. Elsukov static const struct ng_parse_struct_field ng_patch_op_type_fields[]
80426b3d04SJulian Elischer = NG_PATCH_OP_TYPE;
81d359a62dSAndrey V. Elsukov static const struct ng_parse_type ng_patch_op_type = {
82d359a62dSAndrey V. Elsukov &ng_parse_struct_type,
83d359a62dSAndrey V. Elsukov &ng_patch_op_type_fields
84d359a62dSAndrey V. Elsukov };
85d359a62dSAndrey V. Elsukov
86bf909fc9SJulian Elischer static const struct ng_parse_array_info ng_patch_ops_array_info = {
87d359a62dSAndrey V. Elsukov &ng_patch_op_type,
88d359a62dSAndrey V. Elsukov &ng_patch_config_getlen
89d359a62dSAndrey V. Elsukov };
90bf909fc9SJulian Elischer static const struct ng_parse_type ng_patch_ops_array_type = {
91d359a62dSAndrey V. Elsukov &ng_parse_array_type,
92bf909fc9SJulian Elischer &ng_patch_ops_array_info
93d359a62dSAndrey V. Elsukov };
94d359a62dSAndrey V. Elsukov
95d359a62dSAndrey V. Elsukov static const struct ng_parse_struct_field ng_patch_config_type_fields[]
96426b3d04SJulian Elischer = NG_PATCH_CONFIG_TYPE;
97d359a62dSAndrey V. Elsukov static const struct ng_parse_type ng_patch_config_type = {
98d359a62dSAndrey V. Elsukov &ng_parse_struct_type,
99d359a62dSAndrey V. Elsukov &ng_patch_config_type_fields
100d359a62dSAndrey V. Elsukov };
101d359a62dSAndrey V. Elsukov
102d359a62dSAndrey V. Elsukov static const struct ng_parse_struct_field ng_patch_stats_fields[]
103426b3d04SJulian Elischer = NG_PATCH_STATS_TYPE;
104d359a62dSAndrey V. Elsukov static const struct ng_parse_type ng_patch_stats_type = {
105d359a62dSAndrey V. Elsukov &ng_parse_struct_type,
106d359a62dSAndrey V. Elsukov &ng_patch_stats_fields
107d359a62dSAndrey V. Elsukov };
108d359a62dSAndrey V. Elsukov
109d359a62dSAndrey V. Elsukov static const struct ng_cmdlist ng_patch_cmdlist[] = {
110d359a62dSAndrey V. Elsukov {
111d359a62dSAndrey V. Elsukov NGM_PATCH_COOKIE,
112426b3d04SJulian Elischer NGM_PATCH_GETDLT,
113426b3d04SJulian Elischer "getdlt",
114426b3d04SJulian Elischer NULL,
115426b3d04SJulian Elischer &ng_parse_uint8_type
116426b3d04SJulian Elischer },
117426b3d04SJulian Elischer {
118426b3d04SJulian Elischer NGM_PATCH_COOKIE,
119426b3d04SJulian Elischer NGM_PATCH_SETDLT,
120426b3d04SJulian Elischer "setdlt",
121426b3d04SJulian Elischer &ng_parse_uint8_type,
122426b3d04SJulian Elischer NULL
123426b3d04SJulian Elischer },
124426b3d04SJulian Elischer {
125426b3d04SJulian Elischer NGM_PATCH_COOKIE,
126d359a62dSAndrey V. Elsukov NGM_PATCH_GETCONFIG,
127d359a62dSAndrey V. Elsukov "getconfig",
128d359a62dSAndrey V. Elsukov NULL,
129d359a62dSAndrey V. Elsukov &ng_patch_config_type
130d359a62dSAndrey V. Elsukov },
131d359a62dSAndrey V. Elsukov {
132d359a62dSAndrey V. Elsukov NGM_PATCH_COOKIE,
133d359a62dSAndrey V. Elsukov NGM_PATCH_SETCONFIG,
134d359a62dSAndrey V. Elsukov "setconfig",
135d359a62dSAndrey V. Elsukov &ng_patch_config_type,
136d359a62dSAndrey V. Elsukov NULL
137d359a62dSAndrey V. Elsukov },
138d359a62dSAndrey V. Elsukov {
139d359a62dSAndrey V. Elsukov NGM_PATCH_COOKIE,
140d359a62dSAndrey V. Elsukov NGM_PATCH_GET_STATS,
141d359a62dSAndrey V. Elsukov "getstats",
142d359a62dSAndrey V. Elsukov NULL,
143d359a62dSAndrey V. Elsukov &ng_patch_stats_type
144d359a62dSAndrey V. Elsukov },
145d359a62dSAndrey V. Elsukov {
146d359a62dSAndrey V. Elsukov NGM_PATCH_COOKIE,
147d359a62dSAndrey V. Elsukov NGM_PATCH_CLR_STATS,
148d359a62dSAndrey V. Elsukov "clrstats",
149d359a62dSAndrey V. Elsukov NULL,
150d359a62dSAndrey V. Elsukov NULL
151d359a62dSAndrey V. Elsukov },
152d359a62dSAndrey V. Elsukov {
153d359a62dSAndrey V. Elsukov NGM_PATCH_COOKIE,
154d359a62dSAndrey V. Elsukov NGM_PATCH_GETCLR_STATS,
155d359a62dSAndrey V. Elsukov "getclrstats",
156d359a62dSAndrey V. Elsukov NULL,
157d359a62dSAndrey V. Elsukov &ng_patch_stats_type
158d359a62dSAndrey V. Elsukov },
159d359a62dSAndrey V. Elsukov { 0 }
160d359a62dSAndrey V. Elsukov };
161d359a62dSAndrey V. Elsukov
162d359a62dSAndrey V. Elsukov static struct ng_type typestruct = {
163d359a62dSAndrey V. Elsukov .version = NG_ABI_VERSION,
164d359a62dSAndrey V. Elsukov .name = NG_PATCH_NODE_TYPE,
165d359a62dSAndrey V. Elsukov .constructor = ng_patch_constructor,
166d359a62dSAndrey V. Elsukov .rcvmsg = ng_patch_rcvmsg,
167d359a62dSAndrey V. Elsukov .shutdown = ng_patch_shutdown,
168d359a62dSAndrey V. Elsukov .newhook = ng_patch_newhook,
169d359a62dSAndrey V. Elsukov .rcvdata = ng_patch_rcvdata,
170d359a62dSAndrey V. Elsukov .disconnect = ng_patch_disconnect,
171d359a62dSAndrey V. Elsukov .cmdlist = ng_patch_cmdlist,
172d359a62dSAndrey V. Elsukov };
173bf909fc9SJulian Elischer
174d359a62dSAndrey V. Elsukov NETGRAPH_INIT(patch, &typestruct);
175d359a62dSAndrey V. Elsukov
176d359a62dSAndrey V. Elsukov static int
ng_patch_constructor(node_p node)177d359a62dSAndrey V. Elsukov ng_patch_constructor(node_p node)
178d359a62dSAndrey V. Elsukov {
179d359a62dSAndrey V. Elsukov priv_p privdata;
180d359a62dSAndrey V. Elsukov
181ffbfc0aaSAndrey V. Elsukov privdata = malloc(sizeof(*privdata), M_NETGRAPH, M_WAITOK | M_ZERO);
182426b3d04SJulian Elischer privdata->dlt = DLT_RAW;
183426b3d04SJulian Elischer
184d359a62dSAndrey V. Elsukov NG_NODE_SET_PRIVATE(node, privdata);
185426b3d04SJulian Elischer
186d359a62dSAndrey V. Elsukov return (0);
187d359a62dSAndrey V. Elsukov }
188d359a62dSAndrey V. Elsukov
189d359a62dSAndrey V. Elsukov static int
ng_patch_newhook(node_p node,hook_p hook,const char * name)190d359a62dSAndrey V. Elsukov ng_patch_newhook(node_p node, hook_p hook, const char *name)
191d359a62dSAndrey V. Elsukov {
192d359a62dSAndrey V. Elsukov const priv_p privp = NG_NODE_PRIVATE(node);
193d359a62dSAndrey V. Elsukov
194d359a62dSAndrey V. Elsukov if (strncmp(name, NG_PATCH_HOOK_IN, strlen(NG_PATCH_HOOK_IN)) == 0) {
195d359a62dSAndrey V. Elsukov privp->in = hook;
196d359a62dSAndrey V. Elsukov } else if (strncmp(name, NG_PATCH_HOOK_OUT,
197d359a62dSAndrey V. Elsukov strlen(NG_PATCH_HOOK_OUT)) == 0) {
198d359a62dSAndrey V. Elsukov privp->out = hook;
199d359a62dSAndrey V. Elsukov } else
200d359a62dSAndrey V. Elsukov return (EINVAL);
201426b3d04SJulian Elischer
202d359a62dSAndrey V. Elsukov return (0);
203d359a62dSAndrey V. Elsukov }
204d359a62dSAndrey V. Elsukov
205d359a62dSAndrey V. Elsukov static int
ng_patch_rcvmsg(node_p node,item_p item,hook_p lasthook)206d359a62dSAndrey V. Elsukov ng_patch_rcvmsg(node_p node, item_p item, hook_p lasthook)
207d359a62dSAndrey V. Elsukov {
208d359a62dSAndrey V. Elsukov const priv_p privp = NG_NODE_PRIVATE(node);
2096c0a2eb1SAndrey V. Elsukov struct ng_patch_config *conf, *newconf;
210d359a62dSAndrey V. Elsukov struct ng_mesg *msg;
211426b3d04SJulian Elischer struct ng_mesg *resp = NULL;
212426b3d04SJulian Elischer int i, error = 0;
213d359a62dSAndrey V. Elsukov
214d359a62dSAndrey V. Elsukov NGI_GET_MSG(item, msg);
215426b3d04SJulian Elischer
216426b3d04SJulian Elischer if (msg->header.typecookie != NGM_PATCH_COOKIE)
217426b3d04SJulian Elischer ERROUT(EINVAL);
218426b3d04SJulian Elischer
219426b3d04SJulian Elischer switch (msg->header.cmd)
220d359a62dSAndrey V. Elsukov {
221426b3d04SJulian Elischer case NGM_PATCH_GETCONFIG:
222426b3d04SJulian Elischer if (privp->conf == NULL)
223426b3d04SJulian Elischer ERROUT(0);
224426b3d04SJulian Elischer
225426b3d04SJulian Elischer NG_MKRESPONSE(resp, msg,
226426b3d04SJulian Elischer NG_PATCH_CONF_SIZE(privp->conf->count), M_WAITOK);
227426b3d04SJulian Elischer
228426b3d04SJulian Elischer if (resp == NULL)
229426b3d04SJulian Elischer ERROUT(ENOMEM);
230426b3d04SJulian Elischer
231426b3d04SJulian Elischer bcopy(privp->conf, resp->data,
232426b3d04SJulian Elischer NG_PATCH_CONF_SIZE(privp->conf->count));
233426b3d04SJulian Elischer
234426b3d04SJulian Elischer conf = (struct ng_patch_config *) resp->data;
235426b3d04SJulian Elischer
236426b3d04SJulian Elischer for (i = 0; i < conf->count; i++) {
237426b3d04SJulian Elischer switch (conf->ops[i].length)
238426b3d04SJulian Elischer {
239426b3d04SJulian Elischer case 1:
240426b3d04SJulian Elischer conf->ops[i].val.v8 = conf->ops[i].val.v1;
241426b3d04SJulian Elischer break;
242426b3d04SJulian Elischer case 2:
243426b3d04SJulian Elischer conf->ops[i].val.v8 = conf->ops[i].val.v2;
244426b3d04SJulian Elischer break;
245426b3d04SJulian Elischer case 4:
246426b3d04SJulian Elischer conf->ops[i].val.v8 = conf->ops[i].val.v4;
247426b3d04SJulian Elischer break;
248426b3d04SJulian Elischer case 8:
249d359a62dSAndrey V. Elsukov break;
250d359a62dSAndrey V. Elsukov }
251426b3d04SJulian Elischer }
252d359a62dSAndrey V. Elsukov
253426b3d04SJulian Elischer break;
254426b3d04SJulian Elischer
255426b3d04SJulian Elischer case NGM_PATCH_SETCONFIG:
256d359a62dSAndrey V. Elsukov conf = (struct ng_patch_config *) msg->data;
257426b3d04SJulian Elischer
258426b3d04SJulian Elischer if (msg->header.arglen < sizeof(struct ng_patch_config) ||
259426b3d04SJulian Elischer msg->header.arglen < NG_PATCH_CONF_SIZE(conf->count))
260426b3d04SJulian Elischer ERROUT(EINVAL);
261d359a62dSAndrey V. Elsukov
262d359a62dSAndrey V. Elsukov for (i = 0; i < conf->count; i++) {
263426b3d04SJulian Elischer switch (conf->ops[i].length)
264426b3d04SJulian Elischer {
265d359a62dSAndrey V. Elsukov case 1:
266426b3d04SJulian Elischer conf->ops[i].val.v1 = (uint8_t) conf->ops[i].val.v8;
267426b3d04SJulian Elischer break;
268d359a62dSAndrey V. Elsukov case 2:
269426b3d04SJulian Elischer conf->ops[i].val.v2 = (uint16_t) conf->ops[i].val.v8;
270426b3d04SJulian Elischer break;
271d359a62dSAndrey V. Elsukov case 4:
272426b3d04SJulian Elischer conf->ops[i].val.v4 = (uint32_t) conf->ops[i].val.v8;
273426b3d04SJulian Elischer break;
274d359a62dSAndrey V. Elsukov case 8:
275d359a62dSAndrey V. Elsukov break;
276d359a62dSAndrey V. Elsukov default:
277426b3d04SJulian Elischer ERROUT(EINVAL);
278d359a62dSAndrey V. Elsukov }
279d359a62dSAndrey V. Elsukov }
280d359a62dSAndrey V. Elsukov
281426b3d04SJulian Elischer conf->csum_flags &= NG_PATCH_CSUM_IPV4|NG_PATCH_CSUM_IPV6;
282426b3d04SJulian Elischer conf->relative_offset = !!conf->relative_offset;
283d359a62dSAndrey V. Elsukov
284426b3d04SJulian Elischer newconf = malloc(NG_PATCH_CONF_SIZE(conf->count), M_NETGRAPH, M_WAITOK | M_ZERO);
285426b3d04SJulian Elischer
286426b3d04SJulian Elischer bcopy(conf, newconf, NG_PATCH_CONF_SIZE(conf->count));
287426b3d04SJulian Elischer
288426b3d04SJulian Elischer if (privp->conf)
289426b3d04SJulian Elischer free(privp->conf, M_NETGRAPH);
290426b3d04SJulian Elischer
291426b3d04SJulian Elischer privp->conf = newconf;
292426b3d04SJulian Elischer
293d359a62dSAndrey V. Elsukov break;
294426b3d04SJulian Elischer
295d359a62dSAndrey V. Elsukov case NGM_PATCH_GET_STATS:
296d359a62dSAndrey V. Elsukov case NGM_PATCH_CLR_STATS:
297426b3d04SJulian Elischer case NGM_PATCH_GETCLR_STATS:
298426b3d04SJulian Elischer if (msg->header.cmd != NGM_PATCH_CLR_STATS) {
299426b3d04SJulian Elischer NG_MKRESPONSE(resp, msg, sizeof(struct ng_patch_stats), M_WAITOK);
300426b3d04SJulian Elischer
301426b3d04SJulian Elischer if (resp == NULL)
302426b3d04SJulian Elischer ERROUT(ENOMEM);
303426b3d04SJulian Elischer
304426b3d04SJulian Elischer bcopy(&(privp->stats), resp->data, sizeof(struct ng_patch_stats));
305d359a62dSAndrey V. Elsukov }
306d359a62dSAndrey V. Elsukov
307426b3d04SJulian Elischer if (msg->header.cmd != NGM_PATCH_GET_STATS)
308426b3d04SJulian Elischer bzero(&(privp->stats), sizeof(struct ng_patch_stats));
309426b3d04SJulian Elischer
310426b3d04SJulian Elischer break;
311426b3d04SJulian Elischer
312426b3d04SJulian Elischer case NGM_PATCH_GETDLT:
313426b3d04SJulian Elischer NG_MKRESPONSE(resp, msg, sizeof(uint8_t), M_WAITOK);
314426b3d04SJulian Elischer
315426b3d04SJulian Elischer if (resp == NULL)
316426b3d04SJulian Elischer ERROUT(ENOMEM);
317426b3d04SJulian Elischer
318426b3d04SJulian Elischer *((uint8_t *) resp->data) = privp->dlt;
319426b3d04SJulian Elischer
320426b3d04SJulian Elischer break;
321426b3d04SJulian Elischer
322426b3d04SJulian Elischer case NGM_PATCH_SETDLT:
323426b3d04SJulian Elischer if (msg->header.arglen != sizeof(uint8_t))
324426b3d04SJulian Elischer ERROUT(EINVAL);
325426b3d04SJulian Elischer
326426b3d04SJulian Elischer switch (*(uint8_t *) msg->data)
327426b3d04SJulian Elischer {
328426b3d04SJulian Elischer case DLT_EN10MB:
329426b3d04SJulian Elischer case DLT_RAW:
330426b3d04SJulian Elischer privp->dlt = *(uint8_t *) msg->data;
331426b3d04SJulian Elischer break;
332426b3d04SJulian Elischer
333426b3d04SJulian Elischer default:
334426b3d04SJulian Elischer ERROUT(EINVAL);
335426b3d04SJulian Elischer }
336426b3d04SJulian Elischer
337426b3d04SJulian Elischer break;
338426b3d04SJulian Elischer
339426b3d04SJulian Elischer default:
340426b3d04SJulian Elischer ERROUT(EINVAL);
341426b3d04SJulian Elischer }
342426b3d04SJulian Elischer
343426b3d04SJulian Elischer done:
344d359a62dSAndrey V. Elsukov NG_RESPOND_MSG(error, node, item, resp);
345d359a62dSAndrey V. Elsukov NG_FREE_MSG(msg);
346426b3d04SJulian Elischer
347d359a62dSAndrey V. Elsukov return (error);
348d359a62dSAndrey V. Elsukov }
349d359a62dSAndrey V. Elsukov
350d359a62dSAndrey V. Elsukov static void
do_patch(priv_p privp,struct mbuf * m,int global_offset)351426b3d04SJulian Elischer do_patch(priv_p privp, struct mbuf *m, int global_offset)
352d359a62dSAndrey V. Elsukov {
353426b3d04SJulian Elischer int i, offset, patched = 0;
354426b3d04SJulian Elischer union ng_patch_op_val val;
355d359a62dSAndrey V. Elsukov
356426b3d04SJulian Elischer for (i = 0; i < privp->conf->count; i++) {
357426b3d04SJulian Elischer offset = global_offset + privp->conf->ops[i].offset;
358426b3d04SJulian Elischer
359426b3d04SJulian Elischer if (offset + privp->conf->ops[i].length > m->m_pkthdr.len)
360d359a62dSAndrey V. Elsukov continue;
361d359a62dSAndrey V. Elsukov
362d359a62dSAndrey V. Elsukov /* for "=" operation we don't need to copy data from mbuf */
363426b3d04SJulian Elischer if (privp->conf->ops[i].mode != NG_PATCH_MODE_SET)
364426b3d04SJulian Elischer m_copydata(m, offset, privp->conf->ops[i].length, (caddr_t) &val);
365d359a62dSAndrey V. Elsukov
366426b3d04SJulian Elischer switch (privp->conf->ops[i].length)
367426b3d04SJulian Elischer {
368d359a62dSAndrey V. Elsukov case 1:
369426b3d04SJulian Elischer switch (privp->conf->ops[i].mode)
370426b3d04SJulian Elischer {
371d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_SET:
372426b3d04SJulian Elischer val.v1 = privp->conf->ops[i].val.v1;
373d359a62dSAndrey V. Elsukov break;
374d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_ADD:
375426b3d04SJulian Elischer val.v1 += privp->conf->ops[i].val.v1;
376d359a62dSAndrey V. Elsukov break;
377d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_SUB:
378426b3d04SJulian Elischer val.v1 -= privp->conf->ops[i].val.v1;
379d359a62dSAndrey V. Elsukov break;
380d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_MUL:
381426b3d04SJulian Elischer val.v1 *= privp->conf->ops[i].val.v1;
382d359a62dSAndrey V. Elsukov break;
383d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_DIV:
384426b3d04SJulian Elischer val.v1 /= privp->conf->ops[i].val.v1;
385d359a62dSAndrey V. Elsukov break;
386d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_NEG:
387426b3d04SJulian Elischer *((int8_t *) &val) = - *((int8_t *) &val);
388d359a62dSAndrey V. Elsukov break;
389d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_AND:
390426b3d04SJulian Elischer val.v1 &= privp->conf->ops[i].val.v1;
391d359a62dSAndrey V. Elsukov break;
392d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_OR:
393426b3d04SJulian Elischer val.v1 |= privp->conf->ops[i].val.v1;
394d359a62dSAndrey V. Elsukov break;
395d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_XOR:
396426b3d04SJulian Elischer val.v1 ^= privp->conf->ops[i].val.v1;
397d359a62dSAndrey V. Elsukov break;
398d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_SHL:
399426b3d04SJulian Elischer val.v1 <<= privp->conf->ops[i].val.v1;
400d359a62dSAndrey V. Elsukov break;
401d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_SHR:
402426b3d04SJulian Elischer val.v1 >>= privp->conf->ops[i].val.v1;
403d359a62dSAndrey V. Elsukov break;
404d359a62dSAndrey V. Elsukov }
405d359a62dSAndrey V. Elsukov break;
406426b3d04SJulian Elischer
407d359a62dSAndrey V. Elsukov case 2:
408426b3d04SJulian Elischer val.v2 = ntohs(val.v2);
409426b3d04SJulian Elischer
410426b3d04SJulian Elischer switch (privp->conf->ops[i].mode)
411426b3d04SJulian Elischer {
412d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_SET:
413426b3d04SJulian Elischer val.v2 = privp->conf->ops[i].val.v2;
414d359a62dSAndrey V. Elsukov break;
415d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_ADD:
416426b3d04SJulian Elischer val.v2 += privp->conf->ops[i].val.v2;
417d359a62dSAndrey V. Elsukov break;
418d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_SUB:
419426b3d04SJulian Elischer val.v2 -= privp->conf->ops[i].val.v2;
420d359a62dSAndrey V. Elsukov break;
421d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_MUL:
422426b3d04SJulian Elischer val.v2 *= privp->conf->ops[i].val.v2;
423d359a62dSAndrey V. Elsukov break;
424d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_DIV:
425426b3d04SJulian Elischer val.v2 /= privp->conf->ops[i].val.v2;
426d359a62dSAndrey V. Elsukov break;
427d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_NEG:
428426b3d04SJulian Elischer *((int16_t *) &val) = - *((int16_t *) &val);
429d359a62dSAndrey V. Elsukov break;
430d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_AND:
431426b3d04SJulian Elischer val.v2 &= privp->conf->ops[i].val.v2;
432d359a62dSAndrey V. Elsukov break;
433d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_OR:
434426b3d04SJulian Elischer val.v2 |= privp->conf->ops[i].val.v2;
435d359a62dSAndrey V. Elsukov break;
436d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_XOR:
437426b3d04SJulian Elischer val.v2 ^= privp->conf->ops[i].val.v2;
438d359a62dSAndrey V. Elsukov break;
439d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_SHL:
440426b3d04SJulian Elischer val.v2 <<= privp->conf->ops[i].val.v2;
441d359a62dSAndrey V. Elsukov break;
442d359a62dSAndrey V. Elsukov case NG_PATCH_MODE_SHR:
443426b3d04SJulian Elischer val.v2 >>= privp->conf->ops[i].val.v2;
444d359a62dSAndrey V. Elsukov break;
445d359a62dSAndrey V. Elsukov }
446d359a62dSAndrey V. Elsukov
447426b3d04SJulian Elischer val.v2 = htons(val.v2);
448426b3d04SJulian Elischer
449426b3d04SJulian Elischer break;
450426b3d04SJulian Elischer
451426b3d04SJulian Elischer case 4:
452426b3d04SJulian Elischer val.v4 = ntohl(val.v4);
453426b3d04SJulian Elischer
454426b3d04SJulian Elischer switch (privp->conf->ops[i].mode)
455426b3d04SJulian Elischer {
456426b3d04SJulian Elischer case NG_PATCH_MODE_SET:
457426b3d04SJulian Elischer val.v4 = privp->conf->ops[i].val.v4;
458426b3d04SJulian Elischer break;
459426b3d04SJulian Elischer case NG_PATCH_MODE_ADD:
460426b3d04SJulian Elischer val.v4 += privp->conf->ops[i].val.v4;
461426b3d04SJulian Elischer break;
462426b3d04SJulian Elischer case NG_PATCH_MODE_SUB:
463426b3d04SJulian Elischer val.v4 -= privp->conf->ops[i].val.v4;
464426b3d04SJulian Elischer break;
465426b3d04SJulian Elischer case NG_PATCH_MODE_MUL:
466426b3d04SJulian Elischer val.v4 *= privp->conf->ops[i].val.v4;
467426b3d04SJulian Elischer break;
468426b3d04SJulian Elischer case NG_PATCH_MODE_DIV:
469426b3d04SJulian Elischer val.v4 /= privp->conf->ops[i].val.v4;
470426b3d04SJulian Elischer break;
471426b3d04SJulian Elischer case NG_PATCH_MODE_NEG:
472426b3d04SJulian Elischer *((int32_t *) &val) = - *((int32_t *) &val);
473426b3d04SJulian Elischer break;
474426b3d04SJulian Elischer case NG_PATCH_MODE_AND:
475426b3d04SJulian Elischer val.v4 &= privp->conf->ops[i].val.v4;
476426b3d04SJulian Elischer break;
477426b3d04SJulian Elischer case NG_PATCH_MODE_OR:
478426b3d04SJulian Elischer val.v4 |= privp->conf->ops[i].val.v4;
479426b3d04SJulian Elischer break;
480426b3d04SJulian Elischer case NG_PATCH_MODE_XOR:
481426b3d04SJulian Elischer val.v4 ^= privp->conf->ops[i].val.v4;
482426b3d04SJulian Elischer break;
483426b3d04SJulian Elischer case NG_PATCH_MODE_SHL:
484426b3d04SJulian Elischer val.v4 <<= privp->conf->ops[i].val.v4;
485426b3d04SJulian Elischer break;
486426b3d04SJulian Elischer case NG_PATCH_MODE_SHR:
487426b3d04SJulian Elischer val.v4 >>= privp->conf->ops[i].val.v4;
488426b3d04SJulian Elischer break;
489426b3d04SJulian Elischer }
490426b3d04SJulian Elischer
491426b3d04SJulian Elischer val.v4 = htonl(val.v4);
492426b3d04SJulian Elischer
493426b3d04SJulian Elischer break;
494426b3d04SJulian Elischer
495426b3d04SJulian Elischer case 8:
496426b3d04SJulian Elischer val.v8 = be64toh(val.v8);
497426b3d04SJulian Elischer
498426b3d04SJulian Elischer switch (privp->conf->ops[i].mode)
499426b3d04SJulian Elischer {
500426b3d04SJulian Elischer case NG_PATCH_MODE_SET:
501426b3d04SJulian Elischer val.v8 = privp->conf->ops[i].val.v8;
502426b3d04SJulian Elischer break;
503426b3d04SJulian Elischer case NG_PATCH_MODE_ADD:
504426b3d04SJulian Elischer val.v8 += privp->conf->ops[i].val.v8;
505426b3d04SJulian Elischer break;
506426b3d04SJulian Elischer case NG_PATCH_MODE_SUB:
507426b3d04SJulian Elischer val.v8 -= privp->conf->ops[i].val.v8;
508426b3d04SJulian Elischer break;
509426b3d04SJulian Elischer case NG_PATCH_MODE_MUL:
510426b3d04SJulian Elischer val.v8 *= privp->conf->ops[i].val.v8;
511426b3d04SJulian Elischer break;
512426b3d04SJulian Elischer case NG_PATCH_MODE_DIV:
513426b3d04SJulian Elischer val.v8 /= privp->conf->ops[i].val.v8;
514426b3d04SJulian Elischer break;
515426b3d04SJulian Elischer case NG_PATCH_MODE_NEG:
516426b3d04SJulian Elischer *((int64_t *) &val) = - *((int64_t *) &val);
517426b3d04SJulian Elischer break;
518426b3d04SJulian Elischer case NG_PATCH_MODE_AND:
519426b3d04SJulian Elischer val.v8 &= privp->conf->ops[i].val.v8;
520426b3d04SJulian Elischer break;
521426b3d04SJulian Elischer case NG_PATCH_MODE_OR:
522426b3d04SJulian Elischer val.v8 |= privp->conf->ops[i].val.v8;
523426b3d04SJulian Elischer break;
524426b3d04SJulian Elischer case NG_PATCH_MODE_XOR:
525426b3d04SJulian Elischer val.v8 ^= privp->conf->ops[i].val.v8;
526426b3d04SJulian Elischer break;
527426b3d04SJulian Elischer case NG_PATCH_MODE_SHL:
528426b3d04SJulian Elischer val.v8 <<= privp->conf->ops[i].val.v8;
529426b3d04SJulian Elischer break;
530426b3d04SJulian Elischer case NG_PATCH_MODE_SHR:
531426b3d04SJulian Elischer val.v8 >>= privp->conf->ops[i].val.v8;
532426b3d04SJulian Elischer break;
533426b3d04SJulian Elischer }
534426b3d04SJulian Elischer
535426b3d04SJulian Elischer val.v8 = htobe64(val.v8);
536426b3d04SJulian Elischer
537426b3d04SJulian Elischer break;
538426b3d04SJulian Elischer }
539426b3d04SJulian Elischer
540426b3d04SJulian Elischer m_copyback(m, offset, privp->conf->ops[i].length, (caddr_t) &val);
541d359a62dSAndrey V. Elsukov patched = 1;
542d359a62dSAndrey V. Elsukov }
543426b3d04SJulian Elischer
544426b3d04SJulian Elischer if (patched)
545d359a62dSAndrey V. Elsukov privp->stats.patched++;
546d359a62dSAndrey V. Elsukov }
547d359a62dSAndrey V. Elsukov
548d359a62dSAndrey V. Elsukov static int
ng_patch_rcvdata(hook_p hook,item_p item)549d359a62dSAndrey V. Elsukov ng_patch_rcvdata(hook_p hook, item_p item)
550d359a62dSAndrey V. Elsukov {
551d359a62dSAndrey V. Elsukov const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
552d359a62dSAndrey V. Elsukov struct mbuf *m;
553426b3d04SJulian Elischer hook_p out;
554426b3d04SJulian Elischer int pullup_len = 0;
555426b3d04SJulian Elischer int error = 0;
556d359a62dSAndrey V. Elsukov
557d359a62dSAndrey V. Elsukov priv->stats.received++;
558426b3d04SJulian Elischer
559d359a62dSAndrey V. Elsukov NGI_GET_M(item, m);
560426b3d04SJulian Elischer
561426b3d04SJulian Elischer #define PULLUP_CHECK(mbuf, length) do { \
562426b3d04SJulian Elischer pullup_len += length; \
563426b3d04SJulian Elischer if (((mbuf)->m_pkthdr.len < pullup_len) || \
564426b3d04SJulian Elischer (pullup_len > MHLEN)) { \
565426b3d04SJulian Elischer error = EINVAL; \
566426b3d04SJulian Elischer goto bypass; \
567426b3d04SJulian Elischer } \
568426b3d04SJulian Elischer if ((mbuf)->m_len < pullup_len && \
569426b3d04SJulian Elischer (((mbuf) = m_pullup((mbuf), pullup_len)) == NULL)) { \
570426b3d04SJulian Elischer error = ENOBUFS; \
571426b3d04SJulian Elischer goto drop; \
572426b3d04SJulian Elischer } \
573426b3d04SJulian Elischer } while (0)
574426b3d04SJulian Elischer
575426b3d04SJulian Elischer if (priv->conf && hook == priv->in &&
576426b3d04SJulian Elischer m && (m->m_flags & M_PKTHDR)) {
577d359a62dSAndrey V. Elsukov m = m_unshare(m, M_NOWAIT);
578426b3d04SJulian Elischer
579426b3d04SJulian Elischer if (m == NULL)
580426b3d04SJulian Elischer ERROUT(ENOMEM);
581426b3d04SJulian Elischer
582426b3d04SJulian Elischer if (priv->conf->relative_offset) {
583426b3d04SJulian Elischer struct ether_header *eh;
584426b3d04SJulian Elischer struct ng_patch_vlan_header *vh;
585426b3d04SJulian Elischer uint16_t etype;
586426b3d04SJulian Elischer
587426b3d04SJulian Elischer switch (priv->dlt)
588426b3d04SJulian Elischer {
589426b3d04SJulian Elischer case DLT_EN10MB:
590426b3d04SJulian Elischer PULLUP_CHECK(m, sizeof(struct ether_header));
591426b3d04SJulian Elischer eh = mtod(m, struct ether_header *);
592426b3d04SJulian Elischer etype = ntohs(eh->ether_type);
593426b3d04SJulian Elischer
594426b3d04SJulian Elischer for (;;) { /* QinQ support */
595426b3d04SJulian Elischer switch (etype)
596426b3d04SJulian Elischer {
597426b3d04SJulian Elischer case 0x8100:
598426b3d04SJulian Elischer case 0x88A8:
599426b3d04SJulian Elischer case 0x9100:
600426b3d04SJulian Elischer PULLUP_CHECK(m, sizeof(struct ng_patch_vlan_header));
601426b3d04SJulian Elischer vh = (struct ng_patch_vlan_header *) mtodo(m,
602426b3d04SJulian Elischer pullup_len - sizeof(struct ng_patch_vlan_header));
603426b3d04SJulian Elischer etype = ntohs(vh->etype);
604426b3d04SJulian Elischer break;
605426b3d04SJulian Elischer
606426b3d04SJulian Elischer default:
607426b3d04SJulian Elischer goto loopend;
608d359a62dSAndrey V. Elsukov }
609426b3d04SJulian Elischer }
610426b3d04SJulian Elischer loopend:
611426b3d04SJulian Elischer break;
612426b3d04SJulian Elischer
613426b3d04SJulian Elischer case DLT_RAW:
614426b3d04SJulian Elischer break;
615426b3d04SJulian Elischer
616426b3d04SJulian Elischer default:
617426b3d04SJulian Elischer ERROUT(EINVAL);
618426b3d04SJulian Elischer }
619d359a62dSAndrey V. Elsukov }
620d359a62dSAndrey V. Elsukov
621426b3d04SJulian Elischer do_patch(priv, m, pullup_len);
622426b3d04SJulian Elischer
623426b3d04SJulian Elischer m->m_pkthdr.csum_flags |= priv->conf->csum_flags;
624426b3d04SJulian Elischer }
625426b3d04SJulian Elischer
626426b3d04SJulian Elischer #undef PULLUP_CHECK
627426b3d04SJulian Elischer
628426b3d04SJulian Elischer bypass:
629426b3d04SJulian Elischer out = NULL;
630426b3d04SJulian Elischer
631d359a62dSAndrey V. Elsukov if (hook == priv->in) {
632d359a62dSAndrey V. Elsukov /* return frames on 'in' hook if 'out' not connected */
633426b3d04SJulian Elischer out = priv->out ? priv->out : priv->in;
634426b3d04SJulian Elischer } else if (hook == priv->out && priv->in) {
635426b3d04SJulian Elischer /* pass frames on 'out' hook if 'in' connected */
636426b3d04SJulian Elischer out = priv->in;
637d359a62dSAndrey V. Elsukov }
638d359a62dSAndrey V. Elsukov
639426b3d04SJulian Elischer if (out == NULL)
640426b3d04SJulian Elischer ERROUT(0);
641426b3d04SJulian Elischer
642426b3d04SJulian Elischer NG_FWD_NEW_DATA(error, item, out, m);
643426b3d04SJulian Elischer
644426b3d04SJulian Elischer return (error);
645426b3d04SJulian Elischer
646426b3d04SJulian Elischer done:
647426b3d04SJulian Elischer drop:
648d359a62dSAndrey V. Elsukov NG_FREE_ITEM(item);
649d359a62dSAndrey V. Elsukov NG_FREE_M(m);
650426b3d04SJulian Elischer
651426b3d04SJulian Elischer priv->stats.dropped++;
652426b3d04SJulian Elischer
653d359a62dSAndrey V. Elsukov return (error);
654d359a62dSAndrey V. Elsukov }
655d359a62dSAndrey V. Elsukov
656d359a62dSAndrey V. Elsukov static int
ng_patch_shutdown(node_p node)657d359a62dSAndrey V. Elsukov ng_patch_shutdown(node_p node)
658d359a62dSAndrey V. Elsukov {
659d359a62dSAndrey V. Elsukov const priv_p privdata = NG_NODE_PRIVATE(node);
660d359a62dSAndrey V. Elsukov
661d359a62dSAndrey V. Elsukov NG_NODE_SET_PRIVATE(node, NULL);
662d359a62dSAndrey V. Elsukov NG_NODE_UNREF(node);
663426b3d04SJulian Elischer
664426b3d04SJulian Elischer if (privdata->conf != NULL)
665426b3d04SJulian Elischer free(privdata->conf, M_NETGRAPH);
666426b3d04SJulian Elischer
667d359a62dSAndrey V. Elsukov free(privdata, M_NETGRAPH);
668426b3d04SJulian Elischer
669d359a62dSAndrey V. Elsukov return (0);
670d359a62dSAndrey V. Elsukov }
671d359a62dSAndrey V. Elsukov
672d359a62dSAndrey V. Elsukov static int
ng_patch_disconnect(hook_p hook)673d359a62dSAndrey V. Elsukov ng_patch_disconnect(hook_p hook)
674d359a62dSAndrey V. Elsukov {
6756c0a2eb1SAndrey V. Elsukov priv_p priv;
676d359a62dSAndrey V. Elsukov
6776c0a2eb1SAndrey V. Elsukov priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
678426b3d04SJulian Elischer
679d359a62dSAndrey V. Elsukov if (hook == priv->in) {
680d359a62dSAndrey V. Elsukov priv->in = NULL;
681d359a62dSAndrey V. Elsukov }
682426b3d04SJulian Elischer
683d359a62dSAndrey V. Elsukov if (hook == priv->out) {
684d359a62dSAndrey V. Elsukov priv->out = NULL;
685d359a62dSAndrey V. Elsukov }
686426b3d04SJulian Elischer
6876c0a2eb1SAndrey V. Elsukov if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 &&
6886c0a2eb1SAndrey V. Elsukov NG_NODE_IS_VALID(NG_HOOK_NODE(hook))) /* already shutting down? */
689d359a62dSAndrey V. Elsukov ng_rmnode_self(NG_HOOK_NODE(hook));
690426b3d04SJulian Elischer
691d359a62dSAndrey V. Elsukov return (0);
692d359a62dSAndrey V. Elsukov }
693