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