]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/netgraph/ng_patch.c
MFC r368207,368607:
[FreeBSD/stable/10.git] / sys / netgraph / ng_patch.c
1 /*-
2  * Copyright (c) 2010  Maxim Ignatenko <gelraen.ua@gmail.com>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/endian.h>
35 #include <sys/malloc.h>
36 #include <sys/mbuf.h>
37 #include <netgraph/ng_message.h>
38 #include <netgraph/ng_parse.h>
39 #include <netgraph/ng_patch.h>
40 #include <netgraph/netgraph.h>
41
42 static ng_constructor_t ng_patch_constructor;
43 static ng_rcvmsg_t      ng_patch_rcvmsg;
44 static ng_shutdown_t    ng_patch_shutdown;
45 static ng_newhook_t     ng_patch_newhook;
46 static ng_rcvdata_t     ng_patch_rcvdata;
47 static ng_disconnect_t  ng_patch_disconnect;
48
49 static int
50 ng_patch_config_getlen(const struct ng_parse_type *type,
51     const u_char *start, const u_char *buf)
52 {
53         const struct ng_patch_config *conf;
54
55         conf = (const struct ng_patch_config *)(buf -
56             offsetof(struct ng_patch_config, ops));
57
58         return (conf->count);
59 }
60
61 static const struct ng_parse_struct_field ng_patch_op_type_fields[]
62         = NG_PATCH_OP_TYPE_INFO;
63 static const struct ng_parse_type ng_patch_op_type = {
64         &ng_parse_struct_type,
65         &ng_patch_op_type_fields
66 };
67
68 static const struct ng_parse_array_info ng_patch_ops_array_info = {
69         &ng_patch_op_type,
70         &ng_patch_config_getlen
71 };
72 static const struct ng_parse_type ng_patch_ops_array_type = {
73         &ng_parse_array_type,
74         &ng_patch_ops_array_info
75 };
76
77 static const struct ng_parse_struct_field ng_patch_config_type_fields[]
78         = NG_PATCH_CONFIG_TYPE_INFO;
79 static const struct ng_parse_type ng_patch_config_type = {
80         &ng_parse_struct_type,
81         &ng_patch_config_type_fields
82 };
83
84 static const struct ng_parse_struct_field ng_patch_stats_fields[]
85         = NG_PATCH_STATS_TYPE_INFO;
86 static const struct ng_parse_type ng_patch_stats_type = {
87         &ng_parse_struct_type,
88         &ng_patch_stats_fields
89 };
90
91 static const struct ng_cmdlist ng_patch_cmdlist[] = {
92         {
93                 NGM_PATCH_COOKIE,
94                 NGM_PATCH_GETCONFIG,
95                 "getconfig",
96                 NULL,
97                 &ng_patch_config_type
98         },
99         {
100                 NGM_PATCH_COOKIE,
101                 NGM_PATCH_SETCONFIG,
102                 "setconfig",
103                 &ng_patch_config_type,
104                 NULL
105         },
106         {
107                 NGM_PATCH_COOKIE,
108                 NGM_PATCH_GET_STATS,
109                 "getstats",
110                 NULL,
111                 &ng_patch_stats_type
112         },
113         {
114                 NGM_PATCH_COOKIE,
115                 NGM_PATCH_CLR_STATS,
116                 "clrstats",
117                 NULL,
118                 NULL
119         },
120         {
121                 NGM_PATCH_COOKIE,
122                 NGM_PATCH_GETCLR_STATS,
123                 "getclrstats",
124                 NULL,
125                 &ng_patch_stats_type
126         },
127         { 0 }
128 };
129
130 static struct ng_type typestruct = {
131         .version =      NG_ABI_VERSION,
132         .name =         NG_PATCH_NODE_TYPE,
133         .constructor =  ng_patch_constructor,
134         .rcvmsg =       ng_patch_rcvmsg,
135         .shutdown =     ng_patch_shutdown,
136         .newhook =      ng_patch_newhook,
137         .rcvdata =      ng_patch_rcvdata,
138         .disconnect =   ng_patch_disconnect,
139         .cmdlist =      ng_patch_cmdlist,
140 };
141
142 NETGRAPH_INIT(patch, &typestruct);
143
144 union patch_val {
145         uint8_t         v1;
146         uint16_t        v2;
147         uint32_t        v4;
148         uint64_t        v8;
149 };
150
151 /* private data */
152 struct ng_patch_priv {
153         hook_p          in;
154         hook_p          out;
155         struct ng_patch_config *config;
156         union patch_val *val;
157         struct ng_patch_stats stats;
158 };
159 typedef struct ng_patch_priv *priv_p;
160
161 #define NG_PATCH_CONF_SIZE(count)       (sizeof(struct ng_patch_config) + \
162                 (count) * sizeof(struct ng_patch_op))
163
164 static void do_patch(priv_p conf, struct mbuf *m);
165
166 static int
167 ng_patch_constructor(node_p node)
168 {
169         priv_p privdata;
170
171         privdata = malloc(sizeof(*privdata), M_NETGRAPH, M_WAITOK | M_ZERO);
172         NG_NODE_SET_PRIVATE(node, privdata);
173         privdata->in = NULL;
174         privdata->out = NULL;
175         privdata->config = NULL;
176         return (0);
177 }
178
179 static int
180 ng_patch_newhook(node_p node, hook_p hook, const char *name)
181 {
182         const priv_p privp = NG_NODE_PRIVATE(node);
183
184         if (strncmp(name, NG_PATCH_HOOK_IN, strlen(NG_PATCH_HOOK_IN)) == 0) {
185                 privp->in = hook;
186         } else if (strncmp(name, NG_PATCH_HOOK_OUT,
187             strlen(NG_PATCH_HOOK_OUT)) == 0) {
188                 privp->out = hook;
189         } else
190                 return (EINVAL);
191         return(0);
192 }
193
194 static int
195 ng_patch_rcvmsg(node_p node, item_p item, hook_p lasthook)
196 {
197         const priv_p privp = NG_NODE_PRIVATE(node);
198         struct ng_patch_config *conf, *newconf;
199         union patch_val *newval;
200         struct ng_mesg *msg;
201         struct ng_mesg *resp;
202         int i, clear, error;
203
204         clear = error = 0;
205         resp = NULL;
206         NGI_GET_MSG(item, msg);
207         switch (msg->header.typecookie) {
208         case NGM_PATCH_COOKIE:
209                 switch (msg->header.cmd) {
210                 case NGM_PATCH_GETCONFIG:
211                         if (privp->config == NULL)
212                                 break;
213                         NG_MKRESPONSE(resp, msg,
214                             NG_PATCH_CONF_SIZE(privp->config->count),
215                             M_WAITOK);
216                         bcopy(privp->config, resp->data,
217                             NG_PATCH_CONF_SIZE(privp->config->count));
218                         break;
219                 case NGM_PATCH_SETCONFIG:
220                     {
221                         if (msg->header.arglen <
222                             sizeof(struct ng_patch_config)) {
223                                 error = EINVAL;
224                                 break;
225                         }
226
227                         conf = (struct ng_patch_config *)msg->data;
228                         if (msg->header.arglen <
229                             NG_PATCH_CONF_SIZE(conf->count)) {
230                                 error = EINVAL;
231                                 break;
232                         }
233
234                         for(i = 0; i < conf->count; i++) {
235                                 switch(conf->ops[i].length) {
236                                 case 1:
237                                 case 2:
238                                 case 4:
239                                 case 8:
240                                         break;
241                                 default:
242                                         error = EINVAL;
243                                         break;
244                                 }
245                                 if (error != 0)
246                                         break;
247                         }
248
249                         conf->csum_flags &= CSUM_IP | CSUM_TCP | CSUM_UDP |
250                             CSUM_SCTP;
251
252                         if (error == 0) {
253                                 newconf = malloc(
254                                     NG_PATCH_CONF_SIZE(conf->count),
255                                     M_NETGRAPH, M_WAITOK);
256                                 newval = malloc(conf->count *
257                                     sizeof(union patch_val), M_NETGRAPH,
258                                     M_WAITOK);
259                                 for(i = 0; i < conf->count; i++) {
260                                         switch (conf->ops[i].length) {
261                                         case 1:
262                                                 newval[i].v1 =
263                                                     conf->ops[i].value;
264                                                 break;
265                                         case 2:
266                                                 newval[i].v2 =
267                                                     conf->ops[i].value;
268                                                 break;
269                                         case 4:
270                                                 newval[i].v4 =
271                                                     conf->ops[i].value;
272                                                 break;
273                                         case 8:
274                                                 newval[i].v8 =
275                                                     conf->ops[i].value;
276                                                 break;
277                                         }
278                                 }
279                                 bcopy(conf, newconf,
280                                     NG_PATCH_CONF_SIZE(conf->count));
281                                 if (privp->val != NULL)
282                                         free(privp->val, M_NETGRAPH);
283                                 privp->val = newval;
284                                 if (privp->config != NULL)
285                                         free(privp->config, M_NETGRAPH);
286                                 privp->config = newconf;
287                         }
288                         break;
289                     }
290                 case NGM_PATCH_GETCLR_STATS:
291                         clear = 1;
292                         /* FALLTHROUGH */
293                 case NGM_PATCH_GET_STATS:
294                         NG_MKRESPONSE(resp, msg, sizeof(struct ng_patch_stats),
295                             M_WAITOK);
296                         bcopy(&(privp->stats), resp->data,
297                             sizeof(struct ng_patch_stats));
298                         if (clear == 0)
299                                 break;
300                         /* else FALLTHROUGH */
301                 case NGM_PATCH_CLR_STATS:
302                         bzero(&(privp->stats), sizeof(struct ng_patch_stats));
303                         break;
304                 default:
305                         error = EINVAL;
306                         break;
307                 }
308                 break;
309         default:
310                 error = EINVAL;
311                 break;
312         }
313
314         NG_RESPOND_MSG(error, node, item, resp);
315         NG_FREE_MSG(msg);
316         return(error);
317 }
318
319 static void
320 do_patch(priv_p privp, struct mbuf *m)
321 {
322         struct ng_patch_config *conf;
323         uint64_t buf;
324         int i, patched;
325
326         conf = privp->config;
327         patched = 0;
328         for(i = 0; i < conf->count; i++) {
329                 if (conf->ops[i].offset + conf->ops[i].length >
330                     m->m_pkthdr.len)
331                         continue;
332
333                 /* for "=" operation we don't need to copy data from mbuf */
334                 if (conf->ops[i].mode != NG_PATCH_MODE_SET) {
335                         m_copydata(m, conf->ops[i].offset,
336                             conf->ops[i].length, (caddr_t)&buf);
337                 }
338
339                 switch (conf->ops[i].length) {
340                 case 1:
341                         switch (conf->ops[i].mode) {
342                         case NG_PATCH_MODE_SET:
343                                 *((uint8_t *)&buf) = privp->val[i].v1;
344                                 break;
345                         case NG_PATCH_MODE_ADD:
346                                 *((uint8_t *)&buf) += privp->val[i].v1;
347                                 break;
348                         case NG_PATCH_MODE_SUB:
349                                 *((uint8_t *)&buf) -= privp->val[i].v1;
350                                 break;
351                         case NG_PATCH_MODE_MUL:
352                                 *((uint8_t *)&buf) *= privp->val[i].v1;
353                                 break;
354                         case NG_PATCH_MODE_DIV:
355                                 *((uint8_t *)&buf) /= privp->val[i].v1;
356                                 break;
357                         case NG_PATCH_MODE_NEG:
358                                 *((int8_t *)&buf) = - *((int8_t *)&buf);
359                                 break;
360                         case NG_PATCH_MODE_AND:
361                                 *((uint8_t *)&buf) &= privp->val[i].v1;
362                                 break;
363                         case NG_PATCH_MODE_OR:
364                                 *((uint8_t *)&buf) |= privp->val[i].v1;
365                                 break;
366                         case NG_PATCH_MODE_XOR:
367                                 *((uint8_t *)&buf) ^= privp->val[i].v1;
368                                 break;
369                         case NG_PATCH_MODE_SHL:
370                                 *((uint8_t *)&buf) <<= privp->val[i].v1;
371                                 break;
372                         case NG_PATCH_MODE_SHR:
373                                 *((uint8_t *)&buf) >>= privp->val[i].v1;
374                                 break;
375                         }
376                         break;
377                 case 2:
378                         *((int16_t *)&buf) =  ntohs(*((int16_t *)&buf));
379                         switch (conf->ops[i].mode) {
380                         case NG_PATCH_MODE_SET:
381                                 *((uint16_t *)&buf) = privp->val[i].v2;
382                                 break;
383                         case NG_PATCH_MODE_ADD:
384                                 *((uint16_t *)&buf) += privp->val[i].v2;
385                                 break;
386                         case NG_PATCH_MODE_SUB:
387                                 *((uint16_t *)&buf) -= privp->val[i].v2;
388                                 break;
389                         case NG_PATCH_MODE_MUL:
390                                 *((uint16_t *)&buf) *= privp->val[i].v2;
391                                 break;
392                         case NG_PATCH_MODE_DIV:
393                                 *((uint16_t *)&buf) /= privp->val[i].v2;
394                                 break;
395                         case NG_PATCH_MODE_NEG:
396                                 *((int16_t *)&buf) = - *((int16_t *)&buf);
397                                 break;
398                         case NG_PATCH_MODE_AND:
399                                 *((uint16_t *)&buf) &= privp->val[i].v2;
400                                 break;
401                         case NG_PATCH_MODE_OR:
402                                 *((uint16_t *)&buf) |= privp->val[i].v2;
403                                 break;
404                         case NG_PATCH_MODE_XOR:
405                                 *((uint16_t *)&buf) ^= privp->val[i].v2;
406                                 break;
407                         case NG_PATCH_MODE_SHL:
408                                 *((uint16_t *)&buf) <<= privp->val[i].v2;
409                                 break;
410                         case NG_PATCH_MODE_SHR:
411                                 *((uint16_t *)&buf) >>= privp->val[i].v2;
412                                 break;
413                         }
414                         *((int16_t *)&buf) =  htons(*((int16_t *)&buf));
415                         break;
416                 case 4:
417                         *((int32_t *)&buf) =  ntohl(*((int32_t *)&buf));
418                         switch (conf->ops[i].mode) {
419                         case NG_PATCH_MODE_SET:
420                                 *((uint32_t *)&buf) = privp->val[i].v4;
421                                 break;
422                         case NG_PATCH_MODE_ADD:
423                                 *((uint32_t *)&buf) += privp->val[i].v4;
424                                 break;
425                         case NG_PATCH_MODE_SUB:
426                                 *((uint32_t *)&buf) -= privp->val[i].v4;
427                                 break;
428                         case NG_PATCH_MODE_MUL:
429                                 *((uint32_t *)&buf) *= privp->val[i].v4;
430                                 break;
431                         case NG_PATCH_MODE_DIV:
432                                 *((uint32_t *)&buf) /= privp->val[i].v4;
433                                 break;
434                         case NG_PATCH_MODE_NEG:
435                                 *((int32_t *)&buf) = - *((int32_t *)&buf);
436                                 break;
437                         case NG_PATCH_MODE_AND:
438                                 *((uint32_t *)&buf) &= privp->val[i].v4;
439                                 break;
440                         case NG_PATCH_MODE_OR:
441                                 *((uint32_t *)&buf) |= privp->val[i].v4;
442                                 break;
443                         case NG_PATCH_MODE_XOR:
444                                 *((uint32_t *)&buf) ^= privp->val[i].v4;
445                                 break;
446                         case NG_PATCH_MODE_SHL:
447                                 *((uint32_t *)&buf) <<= privp->val[i].v4;
448                                 break;
449                         case NG_PATCH_MODE_SHR:
450                                 *((uint32_t *)&buf) >>= privp->val[i].v4;
451                                 break;
452                         }
453                         *((int32_t *)&buf) =  htonl(*((int32_t *)&buf));
454                         break;
455                 case 8:
456                         *((int64_t *)&buf) =  be64toh(*((int64_t *)&buf));
457                         switch (conf->ops[i].mode) {
458                         case NG_PATCH_MODE_SET:
459                                 *((uint64_t *)&buf) = privp->val[i].v8;
460                                 break;
461                         case NG_PATCH_MODE_ADD:
462                                 *((uint64_t *)&buf) += privp->val[i].v8;
463                                 break;
464                         case NG_PATCH_MODE_SUB:
465                                 *((uint64_t *)&buf) -= privp->val[i].v8;
466                                 break;
467                         case NG_PATCH_MODE_MUL:
468                                 *((uint64_t *)&buf) *= privp->val[i].v8;
469                                 break;
470                         case NG_PATCH_MODE_DIV:
471                                 *((uint64_t *)&buf) /= privp->val[i].v8;
472                                 break;
473                         case NG_PATCH_MODE_NEG:
474                                 *((int64_t *)&buf) = - *((int64_t *)&buf);
475                                 break;
476                         case NG_PATCH_MODE_AND:
477                                 *((uint64_t *)&buf) &= privp->val[i].v8;
478                                 break;
479                         case NG_PATCH_MODE_OR:
480                                 *((uint64_t *)&buf) |= privp->val[i].v8;
481                                 break;
482                         case NG_PATCH_MODE_XOR:
483                                 *((uint64_t *)&buf) ^= privp->val[i].v8;
484                                 break;
485                         case NG_PATCH_MODE_SHL:
486                                 *((uint64_t *)&buf) <<= privp->val[i].v8;
487                                 break;
488                         case NG_PATCH_MODE_SHR:
489                                 *((uint64_t *)&buf) >>= privp->val[i].v8;
490                                 break;
491                         }
492                         *((int64_t *)&buf) =  htobe64(*((int64_t *)&buf));
493                         break;
494                 }
495
496                 m_copyback(m, conf->ops[i].offset, conf->ops[i].length,
497                     (caddr_t)&buf);
498                 patched = 1;
499         }
500         if (patched > 0)
501                 privp->stats.patched++;
502 }
503
504 static int
505 ng_patch_rcvdata(hook_p hook, item_p item)
506 {
507         const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
508         struct mbuf *m;
509         hook_p target;
510         int error;
511
512         priv->stats.received++;
513         NGI_GET_M(item, m);
514         if (priv->config != NULL && hook == priv->in &&
515             (m->m_flags & M_PKTHDR) != 0) {
516                 m = m_unshare(m,M_NOWAIT);
517                 if (m == NULL) {
518                         priv->stats.dropped++;
519                         NG_FREE_ITEM(item);
520                         return (ENOMEM);
521                 }
522                 do_patch(priv, m);
523                 m->m_pkthdr.csum_flags |= priv->config->csum_flags;
524         }
525
526         target = NULL;
527         if (hook == priv->in) {
528                 /* return frames on 'in' hook if 'out' not connected */
529                 if (priv->out != NULL)
530                         target = priv->out;
531                 else
532                         target = priv->in;
533         }
534         if (hook == priv->out && priv->in != NULL)
535                 target = priv->in;
536
537         if (target == NULL) {
538                 priv->stats.dropped++;
539                 NG_FREE_ITEM(item);
540                 NG_FREE_M(m);
541                 return (0);
542         }
543         NG_FWD_NEW_DATA(error, item, target, m);
544         return (error);
545 }
546
547 static int
548 ng_patch_shutdown(node_p node)
549 {
550         const priv_p privdata = NG_NODE_PRIVATE(node);
551
552         if (privdata->val != NULL)
553                 free(privdata->val, M_NETGRAPH);
554         if (privdata->config != NULL)
555                 free(privdata->config, M_NETGRAPH);
556         NG_NODE_SET_PRIVATE(node, NULL);
557         NG_NODE_UNREF(node);
558         free(privdata, M_NETGRAPH);
559         return (0);
560 }
561
562 static int
563 ng_patch_disconnect(hook_p hook)
564 {
565         priv_p priv;
566
567         priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
568         if (hook == priv->in) {
569                 priv->in = NULL;
570         }
571         if (hook == priv->out) {
572                 priv->out = NULL;
573         }
574         if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 &&
575             NG_NODE_IS_VALID(NG_HOOK_NODE(hook))) /* already shutting down? */
576                 ng_rmnode_self(NG_HOOK_NODE(hook));
577         return (0);
578 }
579