Commit | Line | Data |
---|---|---|
b06ebda0 MD |
1 | /* |
2 | * ng_ksocket.c | |
3 | */ | |
4 | ||
5 | /*- | |
6 | * Copyright (c) 1996-1999 Whistle Communications, Inc. | |
7 | * All rights reserved. | |
8 | * | |
9 | * Subject to the following obligations and disclaimer of warranty, use and | |
10 | * redistribution of this software, in source or object code forms, with or | |
11 | * without modifications are expressly permitted by Whistle Communications; | |
12 | * provided, however, that: | |
13 | * 1. Any and all reproductions of the source or object code must include the | |
14 | * copyright notice above and the following disclaimer of warranties; and | |
15 | * 2. No rights are granted, in any manner or form, to use Whistle | |
16 | * Communications, Inc. trademarks, including the mark "WHISTLE | |
17 | * COMMUNICATIONS" on advertising, endorsements, or otherwise except as | |
18 | * such appears in the above copyright notice or in the software. | |
19 | * | |
20 | * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND | |
21 | * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO | |
22 | * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, | |
23 | * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF | |
24 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. | |
25 | * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY | |
26 | * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS | |
27 | * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. | |
28 | * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES | |
29 | * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING | |
30 | * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, | |
31 | * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR | |
32 | * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY | |
33 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
34 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
35 | * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY | |
36 | * OF SUCH DAMAGE. | |
37 | * | |
38 | * Author: Archie Cobbs <archie@freebsd.org> | |
39 | * | |
40 | * $FreeBSD: src/sys/netgraph/ng_ksocket.c,v 1.61 2008/03/07 21:12:56 mav Exp $ | |
41 | * $Whistle: ng_ksocket.c,v 1.1 1999/11/16 20:04:40 archie Exp $ | |
42 | */ | |
43 | ||
44 | /* | |
45 | * Kernel socket node type. This node type is basically a kernel-mode | |
46 | * version of a socket... kindof like the reverse of the socket node type. | |
47 | */ | |
48 | ||
49 | #include <sys/param.h> | |
50 | #include <sys/systm.h> | |
51 | #include <sys/kernel.h> | |
52 | #include <sys/mbuf.h> | |
53 | #include <sys/proc.h> | |
54 | #include <sys/malloc.h> | |
55 | #include <sys/ctype.h> | |
56 | #include <sys/protosw.h> | |
57 | #include <sys/errno.h> | |
58 | #include <sys/socket.h> | |
59 | #include <sys/socketvar.h> | |
60 | #include <sys/uio.h> | |
61 | #include <sys/un.h> | |
62 | ||
5a975a3d MD |
63 | #include "ng_message.h" |
64 | #include "netgraph.h" | |
65 | #include "ng_parse.h" | |
66 | #include "ng_ksocket.h" | |
b06ebda0 MD |
67 | |
68 | #include <netinet/in.h> | |
b06ebda0 MD |
69 | |
70 | #ifdef NG_SEPARATE_MALLOC | |
71 | MALLOC_DEFINE(M_NETGRAPH_KSOCKET, "netgraph_ksock", "netgraph ksock node "); | |
72 | #else | |
73 | #define M_NETGRAPH_KSOCKET M_NETGRAPH | |
74 | #endif | |
75 | ||
76 | #define OFFSETOF(s, e) ((char *)&((s *)0)->e - (char *)((s *)0)) | |
77 | #define SADATA_OFFSET (OFFSETOF(struct sockaddr, sa_data)) | |
78 | ||
79 | /* Node private data */ | |
80 | struct ng_ksocket_private { | |
81 | node_p node; | |
82 | hook_p hook; | |
83 | struct socket *so; | |
84 | int fn_sent; /* FN call on incoming event was sent */ | |
85 | LIST_HEAD(, ng_ksocket_private) embryos; | |
86 | LIST_ENTRY(ng_ksocket_private) siblings; | |
87 | u_int32_t flags; | |
88 | u_int32_t response_token; | |
89 | ng_ID_t response_addr; | |
90 | }; | |
91 | typedef struct ng_ksocket_private *priv_p; | |
92 | ||
93 | /* Flags for priv_p */ | |
94 | #define KSF_CONNECTING 0x00000001 /* Waiting for connection complete */ | |
95 | #define KSF_ACCEPTING 0x00000002 /* Waiting for accept complete */ | |
96 | #define KSF_EOFSEEN 0x00000004 /* Have sent 0-length EOF mbuf */ | |
97 | #define KSF_CLONED 0x00000008 /* Cloned from an accepting socket */ | |
98 | #define KSF_EMBRYONIC 0x00000010 /* Cloned node with no hooks yet */ | |
99 | ||
100 | /* Netgraph node methods */ | |
101 | static ng_constructor_t ng_ksocket_constructor; | |
102 | static ng_rcvmsg_t ng_ksocket_rcvmsg; | |
103 | static ng_shutdown_t ng_ksocket_shutdown; | |
104 | static ng_newhook_t ng_ksocket_newhook; | |
105 | static ng_rcvdata_t ng_ksocket_rcvdata; | |
106 | static ng_connect_t ng_ksocket_connect; | |
107 | static ng_disconnect_t ng_ksocket_disconnect; | |
108 | ||
109 | /* Alias structure */ | |
110 | struct ng_ksocket_alias { | |
111 | const char *name; | |
112 | const int value; | |
113 | const int family; | |
114 | }; | |
115 | ||
116 | /* Protocol family aliases */ | |
117 | static const struct ng_ksocket_alias ng_ksocket_families[] = { | |
118 | { "local", PF_LOCAL }, | |
119 | { "inet", PF_INET }, | |
120 | { "inet6", PF_INET6 }, | |
b06ebda0 MD |
121 | { "ipx", PF_IPX }, |
122 | { "atm", PF_ATM }, | |
123 | { NULL, -1 }, | |
124 | }; | |
125 | ||
126 | /* Socket type aliases */ | |
127 | static const struct ng_ksocket_alias ng_ksocket_types[] = { | |
128 | { "stream", SOCK_STREAM }, | |
129 | { "dgram", SOCK_DGRAM }, | |
130 | { "raw", SOCK_RAW }, | |
131 | { "rdm", SOCK_RDM }, | |
132 | { "seqpacket", SOCK_SEQPACKET }, | |
133 | { NULL, -1 }, | |
134 | }; | |
135 | ||
136 | /* Protocol aliases */ | |
137 | static const struct ng_ksocket_alias ng_ksocket_protos[] = { | |
138 | { "ip", IPPROTO_IP, PF_INET }, | |
139 | { "raw", IPPROTO_RAW, PF_INET }, | |
140 | { "icmp", IPPROTO_ICMP, PF_INET }, | |
141 | { "igmp", IPPROTO_IGMP, PF_INET }, | |
142 | { "tcp", IPPROTO_TCP, PF_INET }, | |
143 | { "udp", IPPROTO_UDP, PF_INET }, | |
144 | { "gre", IPPROTO_GRE, PF_INET }, | |
145 | { "esp", IPPROTO_ESP, PF_INET }, | |
146 | { "ah", IPPROTO_AH, PF_INET }, | |
147 | { "swipe", IPPROTO_SWIPE, PF_INET }, | |
148 | { "encap", IPPROTO_ENCAP, PF_INET }, | |
149 | { "divert", IPPROTO_DIVERT, PF_INET }, | |
150 | { "pim", IPPROTO_PIM, PF_INET }, | |
b06ebda0 MD |
151 | { NULL, -1 }, |
152 | }; | |
153 | ||
154 | /* Helper functions */ | |
155 | static int ng_ksocket_check_accept(priv_p); | |
156 | static void ng_ksocket_finish_accept(priv_p); | |
157 | static void ng_ksocket_incoming(struct socket *so, void *arg, int waitflag); | |
158 | static int ng_ksocket_parse(const struct ng_ksocket_alias *aliases, | |
159 | const char *s, int family); | |
160 | static void ng_ksocket_incoming2(node_p node, hook_p hook, | |
161 | void *arg1, int arg2); | |
162 | ||
163 | /************************************************************************ | |
164 | STRUCT SOCKADDR PARSE TYPE | |
165 | ************************************************************************/ | |
166 | ||
167 | /* Get the length of the data portion of a generic struct sockaddr */ | |
168 | static int | |
169 | ng_parse_generic_sockdata_getLength(const struct ng_parse_type *type, | |
170 | const u_char *start, const u_char *buf) | |
171 | { | |
172 | const struct sockaddr *sa; | |
173 | ||
174 | sa = (const struct sockaddr *)(buf - SADATA_OFFSET); | |
175 | return (sa->sa_len < SADATA_OFFSET) ? 0 : sa->sa_len - SADATA_OFFSET; | |
176 | } | |
177 | ||
178 | /* Type for the variable length data portion of a generic struct sockaddr */ | |
179 | static const struct ng_parse_type ng_ksocket_generic_sockdata_type = { | |
180 | &ng_parse_bytearray_type, | |
181 | &ng_parse_generic_sockdata_getLength | |
182 | }; | |
183 | ||
184 | /* Type for a generic struct sockaddr */ | |
185 | static const struct ng_parse_struct_field | |
186 | ng_parse_generic_sockaddr_type_fields[] = { | |
187 | { "len", &ng_parse_uint8_type }, | |
188 | { "family", &ng_parse_uint8_type }, | |
189 | { "data", &ng_ksocket_generic_sockdata_type }, | |
190 | { NULL } | |
191 | }; | |
192 | static const struct ng_parse_type ng_ksocket_generic_sockaddr_type = { | |
193 | &ng_parse_struct_type, | |
194 | &ng_parse_generic_sockaddr_type_fields | |
195 | }; | |
196 | ||
197 | /* Convert a struct sockaddr from ASCII to binary. If its a protocol | |
198 | family that we specially handle, do that, otherwise defer to the | |
199 | generic parse type ng_ksocket_generic_sockaddr_type. */ | |
200 | static int | |
201 | ng_ksocket_sockaddr_parse(const struct ng_parse_type *type, | |
202 | const char *s, int *off, const u_char *const start, | |
203 | u_char *const buf, int *buflen) | |
204 | { | |
205 | struct sockaddr *const sa = (struct sockaddr *)buf; | |
206 | enum ng_parse_token tok; | |
207 | char fambuf[32]; | |
208 | int family, len; | |
209 | char *t; | |
210 | ||
211 | /* If next token is a left curly brace, use generic parse type */ | |
212 | if ((tok = ng_parse_get_token(s, off, &len)) == T_LBRACE) { | |
213 | return (*ng_ksocket_generic_sockaddr_type.supertype->parse) | |
214 | (&ng_ksocket_generic_sockaddr_type, | |
215 | s, off, start, buf, buflen); | |
216 | } | |
217 | ||
218 | /* Get socket address family followed by a slash */ | |
219 | while (isspace(s[*off])) | |
220 | (*off)++; | |
221 | if ((t = index(s + *off, '/')) == NULL) | |
222 | return (EINVAL); | |
223 | if ((len = t - (s + *off)) > sizeof(fambuf) - 1) | |
224 | return (EINVAL); | |
225 | strncpy(fambuf, s + *off, len); | |
226 | fambuf[len] = '\0'; | |
227 | *off += len + 1; | |
228 | if ((family = ng_ksocket_parse(ng_ksocket_families, fambuf, 0)) == -1) | |
229 | return (EINVAL); | |
230 | ||
231 | /* Set family */ | |
232 | if (*buflen < SADATA_OFFSET) | |
233 | return (ERANGE); | |
234 | sa->sa_family = family; | |
235 | ||
236 | /* Set family-specific data and length */ | |
237 | switch (sa->sa_family) { | |
238 | case PF_LOCAL: /* Get pathname */ | |
239 | { | |
240 | const int pathoff = OFFSETOF(struct sockaddr_un, sun_path); | |
241 | struct sockaddr_un *const sun = (struct sockaddr_un *)sa; | |
242 | int toklen, pathlen; | |
243 | char *path; | |
244 | ||
245 | if ((path = ng_get_string_token(s, off, &toklen, NULL)) == NULL) | |
246 | return (EINVAL); | |
247 | pathlen = strlen(path); | |
248 | if (pathlen > SOCK_MAXADDRLEN) { | |
fc025606 | 249 | kfree(path, M_NETGRAPH_KSOCKET); |
b06ebda0 MD |
250 | return (E2BIG); |
251 | } | |
252 | if (*buflen < pathoff + pathlen) { | |
fc025606 | 253 | kfree(path, M_NETGRAPH_KSOCKET); |
b06ebda0 MD |
254 | return (ERANGE); |
255 | } | |
256 | *off += toklen; | |
257 | bcopy(path, sun->sun_path, pathlen); | |
258 | sun->sun_len = pathoff + pathlen; | |
fc025606 | 259 | kfree(path, M_NETGRAPH_KSOCKET); |
b06ebda0 MD |
260 | break; |
261 | } | |
262 | ||
263 | case PF_INET: /* Get an IP address with optional port */ | |
264 | { | |
265 | struct sockaddr_in *const sin = (struct sockaddr_in *)sa; | |
266 | int i; | |
267 | ||
268 | /* Parse this: <ipaddress>[:port] */ | |
269 | for (i = 0; i < 4; i++) { | |
270 | u_long val; | |
271 | char *eptr; | |
272 | ||
273 | val = strtoul(s + *off, &eptr, 10); | |
274 | if (val > 0xff || eptr == s + *off) | |
275 | return (EINVAL); | |
276 | *off += (eptr - (s + *off)); | |
277 | ((u_char *)&sin->sin_addr)[i] = (u_char)val; | |
278 | if (i < 3) { | |
279 | if (s[*off] != '.') | |
280 | return (EINVAL); | |
281 | (*off)++; | |
282 | } else if (s[*off] == ':') { | |
283 | (*off)++; | |
284 | val = strtoul(s + *off, &eptr, 10); | |
285 | if (val > 0xffff || eptr == s + *off) | |
286 | return (EINVAL); | |
287 | *off += (eptr - (s + *off)); | |
288 | sin->sin_port = htons(val); | |
289 | } else | |
290 | sin->sin_port = 0; | |
291 | } | |
292 | bzero(&sin->sin_zero, sizeof(sin->sin_zero)); | |
293 | sin->sin_len = sizeof(*sin); | |
294 | break; | |
295 | } | |
296 | ||
297 | #if 0 | |
b06ebda0 MD |
298 | case PF_INET6: |
299 | case PF_IPX: | |
300 | #endif | |
301 | ||
302 | default: | |
303 | return (EINVAL); | |
304 | } | |
305 | ||
306 | /* Done */ | |
307 | *buflen = sa->sa_len; | |
308 | return (0); | |
309 | } | |
310 | ||
311 | /* Convert a struct sockaddr from binary to ASCII */ | |
312 | static int | |
313 | ng_ksocket_sockaddr_unparse(const struct ng_parse_type *type, | |
314 | const u_char *data, int *off, char *cbuf, int cbuflen) | |
315 | { | |
316 | const struct sockaddr *sa = (const struct sockaddr *)(data + *off); | |
317 | int slen = 0; | |
318 | ||
319 | /* Output socket address, either in special or generic format */ | |
320 | switch (sa->sa_family) { | |
321 | case PF_LOCAL: | |
322 | { | |
323 | const int pathoff = OFFSETOF(struct sockaddr_un, sun_path); | |
324 | const struct sockaddr_un *sun = (const struct sockaddr_un *)sa; | |
325 | const int pathlen = sun->sun_len - pathoff; | |
326 | char pathbuf[SOCK_MAXADDRLEN + 1]; | |
327 | char *pathtoken; | |
328 | ||
329 | bcopy(sun->sun_path, pathbuf, pathlen); | |
330 | if ((pathtoken = ng_encode_string(pathbuf, pathlen)) == NULL) | |
331 | return (ENOMEM); | |
332 | slen += snprintf(cbuf, cbuflen, "local/%s", pathtoken); | |
fc025606 | 333 | kfree(pathtoken, M_NETGRAPH); |
b06ebda0 MD |
334 | if (slen >= cbuflen) |
335 | return (ERANGE); | |
336 | *off += sun->sun_len; | |
337 | return (0); | |
338 | } | |
339 | ||
340 | case PF_INET: | |
341 | { | |
342 | const struct sockaddr_in *sin = (const struct sockaddr_in *)sa; | |
343 | ||
344 | slen += snprintf(cbuf, cbuflen, "inet/%d.%d.%d.%d", | |
345 | ((const u_char *)&sin->sin_addr)[0], | |
346 | ((const u_char *)&sin->sin_addr)[1], | |
347 | ((const u_char *)&sin->sin_addr)[2], | |
348 | ((const u_char *)&sin->sin_addr)[3]); | |
349 | if (sin->sin_port != 0) { | |
350 | slen += snprintf(cbuf + strlen(cbuf), | |
351 | cbuflen - strlen(cbuf), ":%d", | |
352 | (u_int)ntohs(sin->sin_port)); | |
353 | } | |
354 | if (slen >= cbuflen) | |
355 | return (ERANGE); | |
356 | *off += sizeof(*sin); | |
357 | return(0); | |
358 | } | |
359 | ||
360 | #if 0 | |
b06ebda0 MD |
361 | case PF_INET6: |
362 | case PF_IPX: | |
363 | #endif | |
364 | ||
365 | default: | |
366 | return (*ng_ksocket_generic_sockaddr_type.supertype->unparse) | |
367 | (&ng_ksocket_generic_sockaddr_type, | |
368 | data, off, cbuf, cbuflen); | |
369 | } | |
370 | } | |
371 | ||
372 | /* Parse type for struct sockaddr */ | |
373 | static const struct ng_parse_type ng_ksocket_sockaddr_type = { | |
374 | NULL, | |
375 | NULL, | |
376 | NULL, | |
377 | &ng_ksocket_sockaddr_parse, | |
378 | &ng_ksocket_sockaddr_unparse, | |
379 | NULL /* no such thing as a default struct sockaddr */ | |
380 | }; | |
381 | ||
382 | /************************************************************************ | |
383 | STRUCT NG_KSOCKET_SOCKOPT PARSE TYPE | |
384 | ************************************************************************/ | |
385 | ||
386 | /* Get length of the struct ng_ksocket_sockopt value field, which is the | |
387 | just the excess of the message argument portion over the length of | |
388 | the struct ng_ksocket_sockopt. */ | |
389 | static int | |
390 | ng_parse_sockoptval_getLength(const struct ng_parse_type *type, | |
391 | const u_char *start, const u_char *buf) | |
392 | { | |
393 | static const int offset = OFFSETOF(struct ng_ksocket_sockopt, value); | |
394 | const struct ng_ksocket_sockopt *sopt; | |
395 | const struct ng_mesg *msg; | |
396 | ||
397 | sopt = (const struct ng_ksocket_sockopt *)(buf - offset); | |
398 | msg = (const struct ng_mesg *)((const u_char *)sopt - sizeof(*msg)); | |
399 | return msg->header.arglen - sizeof(*sopt); | |
400 | } | |
401 | ||
402 | /* Parse type for the option value part of a struct ng_ksocket_sockopt | |
403 | XXX Eventually, we should handle the different socket options specially. | |
404 | XXX This would avoid byte order problems, eg an integer value of 1 is | |
405 | XXX going to be "[1]" for little endian or "[3=1]" for big endian. */ | |
406 | static const struct ng_parse_type ng_ksocket_sockoptval_type = { | |
407 | &ng_parse_bytearray_type, | |
408 | &ng_parse_sockoptval_getLength | |
409 | }; | |
410 | ||
411 | /* Parse type for struct ng_ksocket_sockopt */ | |
412 | static const struct ng_parse_struct_field ng_ksocket_sockopt_type_fields[] | |
413 | = NG_KSOCKET_SOCKOPT_INFO(&ng_ksocket_sockoptval_type); | |
414 | static const struct ng_parse_type ng_ksocket_sockopt_type = { | |
415 | &ng_parse_struct_type, | |
416 | &ng_ksocket_sockopt_type_fields | |
417 | }; | |
418 | ||
419 | /* Parse type for struct ng_ksocket_accept */ | |
420 | static const struct ng_parse_struct_field ng_ksocket_accept_type_fields[] | |
421 | = NGM_KSOCKET_ACCEPT_INFO; | |
422 | static const struct ng_parse_type ng_ksocket_accept_type = { | |
423 | &ng_parse_struct_type, | |
424 | &ng_ksocket_accept_type_fields | |
425 | }; | |
426 | ||
427 | /* List of commands and how to convert arguments to/from ASCII */ | |
428 | static const struct ng_cmdlist ng_ksocket_cmds[] = { | |
429 | { | |
430 | NGM_KSOCKET_COOKIE, | |
431 | NGM_KSOCKET_BIND, | |
432 | "bind", | |
433 | &ng_ksocket_sockaddr_type, | |
434 | NULL | |
435 | }, | |
436 | { | |
437 | NGM_KSOCKET_COOKIE, | |
438 | NGM_KSOCKET_LISTEN, | |
439 | "listen", | |
440 | &ng_parse_int32_type, | |
441 | NULL | |
442 | }, | |
443 | { | |
444 | NGM_KSOCKET_COOKIE, | |
445 | NGM_KSOCKET_ACCEPT, | |
446 | "accept", | |
447 | NULL, | |
448 | &ng_ksocket_accept_type | |
449 | }, | |
450 | { | |
451 | NGM_KSOCKET_COOKIE, | |
452 | NGM_KSOCKET_CONNECT, | |
453 | "connect", | |
454 | &ng_ksocket_sockaddr_type, | |
455 | &ng_parse_int32_type | |
456 | }, | |
457 | { | |
458 | NGM_KSOCKET_COOKIE, | |
459 | NGM_KSOCKET_GETNAME, | |
460 | "getname", | |
461 | NULL, | |
462 | &ng_ksocket_sockaddr_type | |
463 | }, | |
464 | { | |
465 | NGM_KSOCKET_COOKIE, | |
466 | NGM_KSOCKET_GETPEERNAME, | |
467 | "getpeername", | |
468 | NULL, | |
469 | &ng_ksocket_sockaddr_type | |
470 | }, | |
471 | { | |
472 | NGM_KSOCKET_COOKIE, | |
473 | NGM_KSOCKET_SETOPT, | |
474 | "setopt", | |
475 | &ng_ksocket_sockopt_type, | |
476 | NULL | |
477 | }, | |
478 | { | |
479 | NGM_KSOCKET_COOKIE, | |
480 | NGM_KSOCKET_GETOPT, | |
481 | "getopt", | |
482 | &ng_ksocket_sockopt_type, | |
483 | &ng_ksocket_sockopt_type | |
484 | }, | |
485 | { 0 } | |
486 | }; | |
487 | ||
488 | /* Node type descriptor */ | |
489 | static struct ng_type ng_ksocket_typestruct = { | |
490 | .version = NG_ABI_VERSION, | |
491 | .name = NG_KSOCKET_NODE_TYPE, | |
492 | .constructor = ng_ksocket_constructor, | |
493 | .rcvmsg = ng_ksocket_rcvmsg, | |
494 | .shutdown = ng_ksocket_shutdown, | |
495 | .newhook = ng_ksocket_newhook, | |
496 | .connect = ng_ksocket_connect, | |
497 | .rcvdata = ng_ksocket_rcvdata, | |
498 | .disconnect = ng_ksocket_disconnect, | |
499 | .cmdlist = ng_ksocket_cmds, | |
500 | }; | |
501 | NETGRAPH_INIT(ksocket, &ng_ksocket_typestruct); | |
502 | ||
503 | #define ERROUT(x) do { error = (x); goto done; } while (0) | |
504 | ||
505 | /************************************************************************ | |
506 | NETGRAPH NODE STUFF | |
507 | ************************************************************************/ | |
508 | ||
509 | /* | |
510 | * Node type constructor | |
511 | * The NODE part is assumed to be all set up. | |
512 | * There is already a reference to the node for us. | |
513 | */ | |
514 | static int | |
515 | ng_ksocket_constructor(node_p node) | |
516 | { | |
517 | priv_p priv; | |
518 | ||
519 | /* Allocate private structure */ | |
fc025606 SW |
520 | priv = kmalloc(sizeof(*priv), M_NETGRAPH, |
521 | M_WAITOK | M_NULLOK | M_ZERO); | |
b06ebda0 MD |
522 | if (priv == NULL) |
523 | return (ENOMEM); | |
524 | ||
525 | LIST_INIT(&priv->embryos); | |
526 | /* cross link them */ | |
527 | priv->node = node; | |
528 | NG_NODE_SET_PRIVATE(node, priv); | |
529 | ||
530 | /* Done */ | |
531 | return (0); | |
532 | } | |
533 | ||
534 | /* | |
535 | * Give our OK for a hook to be added. The hook name is of the | |
536 | * form "<family>/<type>/<proto>" where the three components may | |
537 | * be decimal numbers or else aliases from the above lists. | |
538 | * | |
539 | * Connecting a hook amounts to opening the socket. Disconnecting | |
540 | * the hook closes the socket and destroys the node as well. | |
541 | */ | |
542 | static int | |
543 | ng_ksocket_newhook(node_p node, hook_p hook, const char *name0) | |
544 | { | |
545 | struct thread *td = curthread; /* XXX broken */ | |
546 | const priv_p priv = NG_NODE_PRIVATE(node); | |
547 | char *s1, *s2, name[NG_HOOKSIZ]; | |
548 | int family, type, protocol, error; | |
549 | ||
550 | /* Check if we're already connected */ | |
551 | if (priv->hook != NULL) | |
552 | return (EISCONN); | |
553 | ||
554 | if (priv->flags & KSF_CLONED) { | |
555 | if (priv->flags & KSF_EMBRYONIC) { | |
556 | /* Remove ourselves from our parent's embryo list */ | |
557 | LIST_REMOVE(priv, siblings); | |
558 | priv->flags &= ~KSF_EMBRYONIC; | |
559 | } | |
560 | } else { | |
561 | /* Extract family, type, and protocol from hook name */ | |
562 | snprintf(name, sizeof(name), "%s", name0); | |
563 | s1 = name; | |
564 | if ((s2 = index(s1, '/')) == NULL) | |
565 | return (EINVAL); | |
566 | *s2++ = '\0'; | |
567 | family = ng_ksocket_parse(ng_ksocket_families, s1, 0); | |
568 | if (family == -1) | |
569 | return (EINVAL); | |
570 | s1 = s2; | |
571 | if ((s2 = index(s1, '/')) == NULL) | |
572 | return (EINVAL); | |
573 | *s2++ = '\0'; | |
574 | type = ng_ksocket_parse(ng_ksocket_types, s1, 0); | |
575 | if (type == -1) | |
576 | return (EINVAL); | |
577 | s1 = s2; | |
578 | protocol = ng_ksocket_parse(ng_ksocket_protos, s1, family); | |
579 | if (protocol == -1) | |
580 | return (EINVAL); | |
581 | ||
582 | /* Create the socket */ | |
583 | error = socreate(family, &priv->so, type, protocol, | |
584 | td->td_ucred, td); | |
585 | if (error != 0) | |
586 | return (error); | |
587 | ||
588 | /* XXX call soreserve() ? */ | |
589 | ||
590 | } | |
591 | ||
592 | /* OK */ | |
593 | priv->hook = hook; | |
594 | ||
595 | /* | |
596 | * In case of misconfigured routing a packet may reenter | |
597 | * ksocket node recursively. Decouple stack to avoid possible | |
598 | * panics about sleeping with locks held. | |
599 | */ | |
600 | NG_HOOK_FORCE_QUEUE(hook); | |
601 | ||
602 | return(0); | |
603 | } | |
604 | ||
605 | static int | |
606 | ng_ksocket_connect(hook_p hook) | |
607 | { | |
608 | node_p node = NG_HOOK_NODE(hook); | |
609 | const priv_p priv = NG_NODE_PRIVATE(node); | |
610 | struct socket *const so = priv->so; | |
611 | ||
612 | /* Add our hook for incoming data and other events */ | |
613 | priv->so->so_upcallarg = (caddr_t)node; | |
614 | priv->so->so_upcall = ng_ksocket_incoming; | |
615 | SOCKBUF_LOCK(&priv->so->so_rcv); | |
616 | priv->so->so_rcv.sb_flags |= SB_UPCALL; | |
617 | SOCKBUF_UNLOCK(&priv->so->so_rcv); | |
618 | SOCKBUF_LOCK(&priv->so->so_snd); | |
619 | priv->so->so_snd.sb_flags |= SB_UPCALL; | |
620 | SOCKBUF_UNLOCK(&priv->so->so_snd); | |
621 | SOCK_LOCK(priv->so); | |
6cef7136 | 622 | sosetstate(priv->so, SS_NBIO); |
b06ebda0 MD |
623 | SOCK_UNLOCK(priv->so); |
624 | /* | |
625 | * --Original comment-- | |
626 | * On a cloned socket we may have already received one or more | |
627 | * upcalls which we couldn't handle without a hook. Handle | |
628 | * those now. | |
629 | * We cannot call the upcall function directly | |
630 | * from here, because until this function has returned our | |
631 | * hook isn't connected. | |
632 | * | |
633 | * ---meta comment for -current --- | |
634 | * XXX This is dubius. | |
635 | * Upcalls between the time that the hook was | |
636 | * first created and now (on another processesor) will | |
637 | * be earlier on the queue than the request to finalise the hook. | |
638 | * By the time the hook is finalised, | |
639 | * The queued upcalls will have happenned and the code | |
640 | * will have discarded them because of a lack of a hook. | |
641 | * (socket not open). | |
642 | * | |
643 | * This is a bad byproduct of the complicated way in which hooks | |
644 | * are now created (3 daisy chained async events). | |
645 | * | |
646 | * Since we are a netgraph operation | |
647 | * We know that we hold a lock on this node. This forces the | |
648 | * request we make below to be queued rather than implemented | |
649 | * immediatly which will cause the upcall function to be called a bit | |
650 | * later. | |
651 | * However, as we will run any waiting queued operations immediatly | |
652 | * after doing this one, if we have not finalised the other end | |
653 | * of the hook, those queued operations will fail. | |
654 | */ | |
655 | if (priv->flags & KSF_CLONED) { | |
5a975a3d | 656 | ng_send_fn(node, NULL, &ng_ksocket_incoming2, so, M_WAITOK | M_NULLOK); |
b06ebda0 MD |
657 | } |
658 | ||
659 | return (0); | |
660 | } | |
661 | ||
662 | /* | |
663 | * Receive a control message | |
664 | */ | |
665 | static int | |
666 | ng_ksocket_rcvmsg(node_p node, item_p item, hook_p lasthook) | |
667 | { | |
668 | struct thread *td = curthread; /* XXX broken */ | |
669 | const priv_p priv = NG_NODE_PRIVATE(node); | |
670 | struct socket *const so = priv->so; | |
671 | struct ng_mesg *resp = NULL; | |
672 | int error = 0; | |
673 | struct ng_mesg *msg; | |
674 | ng_ID_t raddr; | |
675 | ||
676 | NGI_GET_MSG(item, msg); | |
677 | switch (msg->header.typecookie) { | |
678 | case NGM_KSOCKET_COOKIE: | |
679 | switch (msg->header.cmd) { | |
680 | case NGM_KSOCKET_BIND: | |
681 | { | |
682 | struct sockaddr *const sa | |
683 | = (struct sockaddr *)msg->data; | |
684 | ||
685 | /* Sanity check */ | |
686 | if (msg->header.arglen < SADATA_OFFSET | |
687 | || msg->header.arglen < sa->sa_len) | |
688 | ERROUT(EINVAL); | |
689 | if (so == NULL) | |
690 | ERROUT(ENXIO); | |
691 | ||
692 | /* Bind */ | |
693 | error = sobind(so, sa, td); | |
694 | break; | |
695 | } | |
696 | case NGM_KSOCKET_LISTEN: | |
697 | { | |
698 | /* Sanity check */ | |
699 | if (msg->header.arglen != sizeof(int32_t)) | |
700 | ERROUT(EINVAL); | |
701 | if (so == NULL) | |
702 | ERROUT(ENXIO); | |
703 | ||
704 | /* Listen */ | |
705 | error = solisten(so, *((int32_t *)msg->data), td); | |
706 | break; | |
707 | } | |
708 | ||
709 | case NGM_KSOCKET_ACCEPT: | |
710 | { | |
711 | /* Sanity check */ | |
712 | if (msg->header.arglen != 0) | |
713 | ERROUT(EINVAL); | |
714 | if (so == NULL) | |
715 | ERROUT(ENXIO); | |
716 | ||
717 | /* Make sure the socket is capable of accepting */ | |
718 | if (!(so->so_options & SO_ACCEPTCONN)) | |
719 | ERROUT(EINVAL); | |
720 | if (priv->flags & KSF_ACCEPTING) | |
721 | ERROUT(EALREADY); | |
722 | ||
723 | error = ng_ksocket_check_accept(priv); | |
724 | if (error != 0 && error != EWOULDBLOCK) | |
725 | ERROUT(error); | |
726 | ||
727 | /* | |
728 | * If a connection is already complete, take it. | |
729 | * Otherwise let the upcall function deal with | |
730 | * the connection when it comes in. | |
731 | */ | |
732 | priv->response_token = msg->header.token; | |
733 | raddr = priv->response_addr = NGI_RETADDR(item); | |
734 | if (error == 0) { | |
735 | ng_ksocket_finish_accept(priv); | |
736 | } else | |
737 | priv->flags |= KSF_ACCEPTING; | |
738 | break; | |
739 | } | |
740 | ||
741 | case NGM_KSOCKET_CONNECT: | |
742 | { | |
743 | struct sockaddr *const sa | |
744 | = (struct sockaddr *)msg->data; | |
745 | ||
746 | /* Sanity check */ | |
747 | if (msg->header.arglen < SADATA_OFFSET | |
748 | || msg->header.arglen < sa->sa_len) | |
749 | ERROUT(EINVAL); | |
750 | if (so == NULL) | |
751 | ERROUT(ENXIO); | |
752 | ||
753 | /* Do connect */ | |
754 | if ((so->so_state & SS_ISCONNECTING) != 0) | |
755 | ERROUT(EALREADY); | |
756 | if ((error = soconnect(so, sa, td)) != 0) { | |
6cef7136 | 757 | soclrstate(so, SS_ISCONNECTING); |
b06ebda0 MD |
758 | ERROUT(error); |
759 | } | |
760 | if ((so->so_state & SS_ISCONNECTING) != 0) { | |
761 | /* We will notify the sender when we connect */ | |
762 | priv->response_token = msg->header.token; | |
763 | raddr = priv->response_addr = NGI_RETADDR(item); | |
764 | priv->flags |= KSF_CONNECTING; | |
765 | ERROUT(EINPROGRESS); | |
766 | } | |
767 | break; | |
768 | } | |
769 | ||
770 | case NGM_KSOCKET_GETNAME: | |
771 | case NGM_KSOCKET_GETPEERNAME: | |
772 | { | |
773 | int (*func)(struct socket *so, struct sockaddr **nam); | |
774 | struct sockaddr *sa = NULL; | |
775 | int len; | |
776 | ||
777 | /* Sanity check */ | |
778 | if (msg->header.arglen != 0) | |
779 | ERROUT(EINVAL); | |
780 | if (so == NULL) | |
781 | ERROUT(ENXIO); | |
782 | ||
783 | /* Get function */ | |
784 | if (msg->header.cmd == NGM_KSOCKET_GETPEERNAME) { | |
785 | if ((so->so_state | |
786 | & (SS_ISCONNECTED|SS_ISCONFIRMING)) == 0) | |
787 | ERROUT(ENOTCONN); | |
788 | func = so->so_proto->pr_usrreqs->pru_peeraddr; | |
789 | } else | |
790 | func = so->so_proto->pr_usrreqs->pru_sockaddr; | |
791 | ||
792 | /* Get local or peer address */ | |
793 | if ((error = (*func)(so, &sa)) != 0) | |
794 | goto bail; | |
795 | len = (sa == NULL) ? 0 : sa->sa_len; | |
796 | ||
797 | /* Send it back in a response */ | |
5a975a3d | 798 | NG_MKRESPONSE(resp, msg, len, M_WAITOK | M_NULLOK); |
b06ebda0 MD |
799 | if (resp == NULL) { |
800 | error = ENOMEM; | |
801 | goto bail; | |
802 | } | |
803 | bcopy(sa, resp->data, len); | |
804 | ||
805 | bail: | |
806 | /* Cleanup */ | |
807 | if (sa != NULL) | |
fc025606 | 808 | kfree(sa, M_SONAME); |
b06ebda0 MD |
809 | break; |
810 | } | |
811 | ||
812 | case NGM_KSOCKET_GETOPT: | |
813 | { | |
814 | struct ng_ksocket_sockopt *ksopt = | |
815 | (struct ng_ksocket_sockopt *)msg->data; | |
816 | struct sockopt sopt; | |
817 | ||
818 | /* Sanity check */ | |
819 | if (msg->header.arglen != sizeof(*ksopt)) | |
820 | ERROUT(EINVAL); | |
821 | if (so == NULL) | |
822 | ERROUT(ENXIO); | |
823 | ||
824 | /* Get response with room for option value */ | |
825 | NG_MKRESPONSE(resp, msg, sizeof(*ksopt) | |
5a975a3d | 826 | + NG_KSOCKET_MAX_OPTLEN, M_WAITOK | M_NULLOK); |
b06ebda0 MD |
827 | if (resp == NULL) |
828 | ERROUT(ENOMEM); | |
829 | ||
830 | /* Get socket option, and put value in the response */ | |
831 | sopt.sopt_dir = SOPT_GET; | |
832 | sopt.sopt_level = ksopt->level; | |
833 | sopt.sopt_name = ksopt->name; | |
834 | sopt.sopt_td = NULL; | |
835 | sopt.sopt_valsize = NG_KSOCKET_MAX_OPTLEN; | |
836 | ksopt = (struct ng_ksocket_sockopt *)resp->data; | |
837 | sopt.sopt_val = ksopt->value; | |
838 | if ((error = sogetopt(so, &sopt)) != 0) { | |
839 | NG_FREE_MSG(resp); | |
840 | break; | |
841 | } | |
842 | ||
843 | /* Set actual value length */ | |
844 | resp->header.arglen = sizeof(*ksopt) | |
845 | + sopt.sopt_valsize; | |
846 | break; | |
847 | } | |
848 | ||
849 | case NGM_KSOCKET_SETOPT: | |
850 | { | |
851 | struct ng_ksocket_sockopt *const ksopt = | |
852 | (struct ng_ksocket_sockopt *)msg->data; | |
853 | const int valsize = msg->header.arglen - sizeof(*ksopt); | |
854 | struct sockopt sopt; | |
855 | ||
856 | /* Sanity check */ | |
857 | if (valsize < 0) | |
858 | ERROUT(EINVAL); | |
859 | if (so == NULL) | |
860 | ERROUT(ENXIO); | |
861 | ||
862 | /* Set socket option */ | |
863 | sopt.sopt_dir = SOPT_SET; | |
864 | sopt.sopt_level = ksopt->level; | |
865 | sopt.sopt_name = ksopt->name; | |
866 | sopt.sopt_val = ksopt->value; | |
867 | sopt.sopt_valsize = valsize; | |
868 | sopt.sopt_td = NULL; | |
869 | error = sosetopt(so, &sopt); | |
870 | break; | |
871 | } | |
872 | ||
873 | default: | |
874 | error = EINVAL; | |
875 | break; | |
876 | } | |
877 | break; | |
878 | default: | |
879 | error = EINVAL; | |
880 | break; | |
881 | } | |
882 | done: | |
883 | NG_RESPOND_MSG(error, node, item, resp); | |
884 | NG_FREE_MSG(msg); | |
885 | return (error); | |
886 | } | |
887 | ||
888 | /* | |
889 | * Receive incoming data on our hook. Send it out the socket. | |
890 | */ | |
891 | static int | |
892 | ng_ksocket_rcvdata(hook_p hook, item_p item) | |
893 | { | |
894 | struct thread *td = curthread; /* XXX broken */ | |
895 | const node_p node = NG_HOOK_NODE(hook); | |
896 | const priv_p priv = NG_NODE_PRIVATE(node); | |
897 | struct socket *const so = priv->so; | |
898 | struct sockaddr *sa = NULL; | |
899 | int error; | |
900 | struct mbuf *m; | |
901 | struct sa_tag *stag; | |
902 | ||
903 | /* Extract data */ | |
904 | NGI_GET_M(item, m); | |
905 | NG_FREE_ITEM(item); | |
906 | ||
907 | /* | |
908 | * Look if socket address is stored in packet tags. | |
909 | * If sockaddr is ours, or provided by a third party (zero id), | |
910 | * then we accept it. | |
911 | */ | |
912 | if (((stag = (struct sa_tag *)m_tag_locate(m, NGM_KSOCKET_COOKIE, | |
913 | NG_KSOCKET_TAG_SOCKADDR, NULL)) != NULL) && | |
914 | (stag->id == NG_NODE_ID(node) || stag->id == 0)) | |
915 | sa = &stag->sa; | |
916 | ||
917 | /* Reset specific mbuf flags to prevent addressing problems. */ | |
918 | m->m_flags &= ~(M_BCAST|M_MCAST); | |
919 | ||
920 | /* Send packet */ | |
921 | error = sosend(so, sa, 0, m, 0, 0, td); | |
922 | ||
923 | return (error); | |
924 | } | |
925 | ||
926 | /* | |
927 | * Destroy node | |
928 | */ | |
929 | static int | |
930 | ng_ksocket_shutdown(node_p node) | |
931 | { | |
932 | const priv_p priv = NG_NODE_PRIVATE(node); | |
933 | priv_p embryo; | |
934 | ||
935 | /* Close our socket (if any) */ | |
936 | if (priv->so != NULL) { | |
937 | SOCKBUF_LOCK(&priv->so->so_rcv); | |
938 | priv->so->so_rcv.sb_flags &= ~SB_UPCALL; | |
939 | SOCKBUF_UNLOCK(&priv->so->so_rcv); | |
940 | SOCKBUF_LOCK(&priv->so->so_snd); | |
941 | priv->so->so_snd.sb_flags &= ~SB_UPCALL; | |
942 | SOCKBUF_UNLOCK(&priv->so->so_snd); | |
943 | priv->so->so_upcall = NULL; | |
944 | soclose(priv->so); | |
945 | priv->so = NULL; | |
946 | } | |
947 | ||
948 | /* If we are an embryo, take ourselves out of the parent's list */ | |
949 | if (priv->flags & KSF_EMBRYONIC) { | |
950 | LIST_REMOVE(priv, siblings); | |
951 | priv->flags &= ~KSF_EMBRYONIC; | |
952 | } | |
953 | ||
954 | /* Remove any embryonic children we have */ | |
955 | while (!LIST_EMPTY(&priv->embryos)) { | |
956 | embryo = LIST_FIRST(&priv->embryos); | |
957 | ng_rmnode_self(embryo->node); | |
958 | } | |
959 | ||
960 | /* Take down netgraph node */ | |
961 | bzero(priv, sizeof(*priv)); | |
fc025606 | 962 | kfree(priv, M_NETGRAPH); |
b06ebda0 MD |
963 | NG_NODE_SET_PRIVATE(node, NULL); |
964 | NG_NODE_UNREF(node); /* let the node escape */ | |
965 | return (0); | |
966 | } | |
967 | ||
968 | /* | |
969 | * Hook disconnection | |
970 | */ | |
971 | static int | |
972 | ng_ksocket_disconnect(hook_p hook) | |
973 | { | |
974 | KASSERT(NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0, | |
975 | ("%s: numhooks=%d?", __func__, | |
976 | NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)))); | |
977 | if (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))) | |
978 | ng_rmnode_self(NG_HOOK_NODE(hook)); | |
979 | return (0); | |
980 | } | |
981 | ||
982 | /************************************************************************ | |
983 | HELPER STUFF | |
984 | ************************************************************************/ | |
985 | /* | |
986 | * You should not "just call" a netgraph node function from an external | |
987 | * asynchronous event. This is because in doing so you are ignoring the | |
988 | * locking on the netgraph nodes. Instead call your function via ng_send_fn(). | |
989 | * This will call the function you chose, but will first do all the | |
990 | * locking rigmarole. Your function MAY only be called at some distant future | |
991 | * time (several millisecs away) so don't give it any arguments | |
992 | * that may be revoked soon (e.g. on your stack). | |
993 | * | |
994 | * To decouple stack, we use queue version of ng_send_fn(). | |
995 | */ | |
996 | ||
997 | static void | |
998 | ng_ksocket_incoming(struct socket *so, void *arg, int waitflag) | |
999 | { | |
1000 | const node_p node = arg; | |
1001 | const priv_p priv = NG_NODE_PRIVATE(node); | |
1002 | int wait = ((waitflag & M_WAITOK) ? NG_WAITOK : 0) | NG_QUEUE; | |
1003 | ||
1004 | /* | |
1005 | * Even if node is not locked, as soon as we are called, we assume | |
1006 | * it exist and it's private area is valid. With some care we can | |
1007 | * access it. Mark node that incoming event for it was sent to | |
1008 | * avoid unneded queue trashing. | |
1009 | */ | |
1010 | if (atomic_cmpset_int(&priv->fn_sent, 0, 1) && | |
1011 | ng_send_fn1(node, NULL, &ng_ksocket_incoming2, so, 0, wait)) { | |
1012 | atomic_store_rel_int(&priv->fn_sent, 0); | |
1013 | } | |
1014 | } | |
1015 | ||
1016 | ||
1017 | /* | |
1018 | * When incoming data is appended to the socket, we get notified here. | |
1019 | * This is also called whenever a significant event occurs for the socket. | |
1020 | * Our original caller may have queued this even some time ago and | |
1021 | * we cannot trust that he even still exists. The node however is being | |
1022 | * held with a reference by the queueing code and guarantied to be valid. | |
1023 | */ | |
1024 | static void | |
1025 | ng_ksocket_incoming2(node_p node, hook_p hook, void *arg1, int arg2) | |
1026 | { | |
1027 | struct socket *so = arg1; | |
1028 | const priv_p priv = NG_NODE_PRIVATE(node); | |
1029 | struct mbuf *m; | |
1030 | struct ng_mesg *response; | |
1031 | struct uio auio; | |
1032 | int s, flags, error; | |
1033 | ||
1034 | s = splnet(); | |
1035 | ||
1036 | /* so = priv->so; *//* XXX could have derived this like so */ | |
1037 | KASSERT(so == priv->so, ("%s: wrong socket", __func__)); | |
1038 | ||
1039 | /* Allow next incoming event to be queued. */ | |
1040 | atomic_store_rel_int(&priv->fn_sent, 0); | |
1041 | ||
1042 | /* Check whether a pending connect operation has completed */ | |
1043 | if (priv->flags & KSF_CONNECTING) { | |
1044 | if ((error = so->so_error) != 0) { | |
1045 | so->so_error = 0; | |
6cef7136 | 1046 | soclrstate(so, SS_ISCONNECTING); |
b06ebda0 MD |
1047 | } |
1048 | if (!(so->so_state & SS_ISCONNECTING)) { | |
1049 | NG_MKMESSAGE(response, NGM_KSOCKET_COOKIE, | |
5a975a3d | 1050 | NGM_KSOCKET_CONNECT, sizeof(int32_t), M_WAITOK | M_NULLOK); |
b06ebda0 MD |
1051 | if (response != NULL) { |
1052 | response->header.flags |= NGF_RESP; | |
1053 | response->header.token = priv->response_token; | |
1054 | *(int32_t *)response->data = error; | |
1055 | /* | |
1056 | * send an async "response" message | |
1057 | * to the node that set us up | |
1058 | * (if it still exists) | |
1059 | */ | |
1060 | NG_SEND_MSG_ID(error, node, | |
1061 | response, priv->response_addr, 0); | |
1062 | } | |
1063 | priv->flags &= ~KSF_CONNECTING; | |
1064 | } | |
1065 | } | |
1066 | ||
1067 | /* Check whether a pending accept operation has completed */ | |
1068 | if (priv->flags & KSF_ACCEPTING) { | |
1069 | error = ng_ksocket_check_accept(priv); | |
1070 | if (error != EWOULDBLOCK) | |
1071 | priv->flags &= ~KSF_ACCEPTING; | |
1072 | if (error == 0) | |
1073 | ng_ksocket_finish_accept(priv); | |
1074 | } | |
1075 | ||
1076 | /* | |
1077 | * If we don't have a hook, we must handle data events later. When | |
1078 | * the hook gets created and is connected, this upcall function | |
1079 | * will be called again. | |
1080 | */ | |
1081 | if (priv->hook == NULL) { | |
1082 | splx(s); | |
1083 | return; | |
1084 | } | |
1085 | ||
1086 | /* Read and forward available mbuf's */ | |
1087 | auio.uio_td = NULL; | |
1088 | auio.uio_resid = 1000000000; | |
1089 | flags = MSG_DONTWAIT; | |
1090 | while (1) { | |
1091 | struct sockaddr *sa = NULL; | |
1092 | struct mbuf *n; | |
1093 | ||
1094 | /* Try to get next packet from socket */ | |
1095 | if ((error = soreceive(so, (so->so_state & SS_ISCONNECTED) ? | |
60233e58 | 1096 | NULL : &sa, &auio, &m, NULL, &flags)) != 0) |
b06ebda0 MD |
1097 | break; |
1098 | ||
1099 | /* See if we got anything */ | |
1100 | if (m == NULL) { | |
1101 | if (sa != NULL) | |
fc025606 | 1102 | kfree(sa, M_SONAME); |
b06ebda0 MD |
1103 | break; |
1104 | } | |
1105 | ||
1106 | /* | |
1107 | * Don't trust the various socket layers to get the | |
1108 | * packet header and length correct (e.g. kern/15175). | |
1109 | * | |
1110 | * Also, do not trust that soreceive() will clear m_nextpkt | |
1111 | * for us (e.g. kern/84952, kern/82413). | |
1112 | */ | |
1113 | m->m_pkthdr.csum_flags = 0; | |
1114 | for (n = m, m->m_pkthdr.len = 0; n != NULL; n = n->m_next) { | |
1115 | m->m_pkthdr.len += n->m_len; | |
1116 | n->m_nextpkt = NULL; | |
1117 | } | |
1118 | ||
1119 | /* Put peer's socket address (if any) into a tag */ | |
1120 | if (sa != NULL) { | |
1121 | struct sa_tag *stag; | |
1122 | ||
1123 | stag = (struct sa_tag *)m_tag_alloc(NGM_KSOCKET_COOKIE, | |
1124 | NG_KSOCKET_TAG_SOCKADDR, sizeof(ng_ID_t) + | |
5a975a3d | 1125 | sa->sa_len, MB_DONTWAIT); |
b06ebda0 | 1126 | if (stag == NULL) { |
fc025606 | 1127 | kfree(sa, M_SONAME); |
b06ebda0 MD |
1128 | goto sendit; |
1129 | } | |
1130 | bcopy(sa, &stag->sa, sa->sa_len); | |
fc025606 | 1131 | kfree(sa, M_SONAME); |
b06ebda0 MD |
1132 | stag->id = NG_NODE_ID(node); |
1133 | m_tag_prepend(m, &stag->tag); | |
1134 | } | |
1135 | ||
1136 | sendit: /* Forward data with optional peer sockaddr as packet tag */ | |
1137 | NG_SEND_DATA_ONLY(error, priv->hook, m); | |
1138 | } | |
1139 | ||
1140 | /* | |
1141 | * If the peer has closed the connection, forward a 0-length mbuf | |
1142 | * to indicate end-of-file. | |
1143 | */ | |
1144 | if (so->so_rcv.sb_state & SBS_CANTRCVMORE && !(priv->flags & KSF_EOFSEEN)) { | |
5a975a3d | 1145 | MGETHDR(m, MB_DONTWAIT, MT_DATA); |
b06ebda0 MD |
1146 | if (m != NULL) { |
1147 | m->m_len = m->m_pkthdr.len = 0; | |
1148 | NG_SEND_DATA_ONLY(error, priv->hook, m); | |
1149 | } | |
1150 | priv->flags |= KSF_EOFSEEN; | |
1151 | } | |
1152 | splx(s); | |
1153 | } | |
1154 | ||
1155 | /* | |
1156 | * Check for a completed incoming connection and return 0 if one is found. | |
1157 | * Otherwise return the appropriate error code. | |
1158 | */ | |
1159 | static int | |
1160 | ng_ksocket_check_accept(priv_p priv) | |
1161 | { | |
1162 | struct socket *const head = priv->so; | |
1163 | int error; | |
1164 | ||
1165 | if ((error = head->so_error) != 0) { | |
1166 | head->so_error = 0; | |
1167 | return error; | |
1168 | } | |
1169 | /* Unlocked read. */ | |
1170 | if (TAILQ_EMPTY(&head->so_comp)) { | |
1171 | if (head->so_rcv.sb_state & SBS_CANTRCVMORE) | |
1172 | return ECONNABORTED; | |
1173 | return EWOULDBLOCK; | |
1174 | } | |
1175 | return 0; | |
1176 | } | |
1177 | ||
1178 | /* | |
1179 | * Handle the first completed incoming connection, assumed to be already | |
1180 | * on the socket's so_comp queue. | |
1181 | */ | |
1182 | static void | |
1183 | ng_ksocket_finish_accept(priv_p priv) | |
1184 | { | |
1185 | struct socket *const head = priv->so; | |
1186 | struct socket *so; | |
1187 | struct sockaddr *sa = NULL; | |
1188 | struct ng_mesg *resp; | |
1189 | struct ng_ksocket_accept *resp_data; | |
1190 | node_p node; | |
1191 | priv_p priv2; | |
1192 | int len; | |
1193 | int error; | |
1194 | ||
1195 | ACCEPT_LOCK(); | |
1196 | so = TAILQ_FIRST(&head->so_comp); | |
1197 | if (so == NULL) { /* Should never happen */ | |
1198 | ACCEPT_UNLOCK(); | |
1199 | return; | |
1200 | } | |
1201 | TAILQ_REMOVE(&head->so_comp, so, so_list); | |
1202 | head->so_qlen--; | |
1203 | so->so_qstate &= ~SQ_COMP; | |
1204 | so->so_head = NULL; | |
1205 | SOCK_LOCK(so); | |
1206 | soref(so); | |
6cef7136 | 1207 | sosetstate(so, SS_NBIO); |
b06ebda0 MD |
1208 | SOCK_UNLOCK(so); |
1209 | ACCEPT_UNLOCK(); | |
1210 | ||
1211 | /* XXX KNOTE(&head->so_rcv.sb_sel.si_note, 0); */ | |
1212 | ||
1213 | soaccept(so, &sa); | |
1214 | ||
1215 | len = OFFSETOF(struct ng_ksocket_accept, addr); | |
1216 | if (sa != NULL) | |
1217 | len += sa->sa_len; | |
1218 | ||
1219 | NG_MKMESSAGE(resp, NGM_KSOCKET_COOKIE, NGM_KSOCKET_ACCEPT, len, | |
5a975a3d | 1220 | M_WAITOK | M_NULLOK); |
b06ebda0 MD |
1221 | if (resp == NULL) { |
1222 | soclose(so); | |
1223 | goto out; | |
1224 | } | |
1225 | resp->header.flags |= NGF_RESP; | |
1226 | resp->header.token = priv->response_token; | |
1227 | ||
1228 | /* Clone a ksocket node to wrap the new socket */ | |
1229 | error = ng_make_node_common(&ng_ksocket_typestruct, &node); | |
1230 | if (error) { | |
fc025606 | 1231 | kfree(resp, M_NETGRAPH); |
b06ebda0 MD |
1232 | soclose(so); |
1233 | goto out; | |
1234 | } | |
1235 | ||
1236 | if (ng_ksocket_constructor(node) != 0) { | |
1237 | NG_NODE_UNREF(node); | |
fc025606 | 1238 | kfree(resp, M_NETGRAPH); |
b06ebda0 MD |
1239 | soclose(so); |
1240 | goto out; | |
1241 | } | |
1242 | ||
1243 | priv2 = NG_NODE_PRIVATE(node); | |
1244 | priv2->so = so; | |
1245 | priv2->flags |= KSF_CLONED | KSF_EMBRYONIC; | |
1246 | ||
1247 | /* | |
1248 | * Insert the cloned node into a list of embryonic children | |
1249 | * on the parent node. When a hook is created on the cloned | |
1250 | * node it will be removed from this list. When the parent | |
1251 | * is destroyed it will destroy any embryonic children it has. | |
1252 | */ | |
1253 | LIST_INSERT_HEAD(&priv->embryos, priv2, siblings); | |
1254 | ||
1255 | so->so_upcallarg = (caddr_t)node; | |
1256 | so->so_upcall = ng_ksocket_incoming; | |
1257 | SOCKBUF_LOCK(&so->so_rcv); | |
1258 | so->so_rcv.sb_flags |= SB_UPCALL; | |
1259 | SOCKBUF_UNLOCK(&so->so_rcv); | |
1260 | SOCKBUF_LOCK(&so->so_snd); | |
1261 | so->so_snd.sb_flags |= SB_UPCALL; | |
1262 | SOCKBUF_UNLOCK(&so->so_snd); | |
1263 | ||
1264 | /* Fill in the response data and send it or return it to the caller */ | |
1265 | resp_data = (struct ng_ksocket_accept *)resp->data; | |
1266 | resp_data->nodeid = NG_NODE_ID(node); | |
1267 | if (sa != NULL) | |
1268 | bcopy(sa, &resp_data->addr, sa->sa_len); | |
1269 | NG_SEND_MSG_ID(error, node, resp, priv->response_addr, 0); | |
1270 | ||
1271 | out: | |
1272 | if (sa != NULL) | |
fc025606 | 1273 | kfree(sa, M_SONAME); |
b06ebda0 MD |
1274 | } |
1275 | ||
1276 | /* | |
1277 | * Parse out either an integer value or an alias. | |
1278 | */ | |
1279 | static int | |
1280 | ng_ksocket_parse(const struct ng_ksocket_alias *aliases, | |
1281 | const char *s, int family) | |
1282 | { | |
1283 | int k, val; | |
1284 | char *eptr; | |
1285 | ||
1286 | /* Try aliases */ | |
1287 | for (k = 0; aliases[k].name != NULL; k++) { | |
1288 | if (strcmp(s, aliases[k].name) == 0 | |
1289 | && aliases[k].family == family) | |
1290 | return aliases[k].value; | |
1291 | } | |
1292 | ||
1293 | /* Try parsing as a number */ | |
1294 | val = (int)strtoul(s, &eptr, 10); | |
1295 | if (val < 0 || *eptr != '\0') | |
1296 | return (-1); | |
1297 | return (val); | |
1298 | } | |
1299 |