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