diff options
Diffstat (limited to 'sys/netgraph/ng_ksocket.c')
-rw-r--r-- | sys/netgraph/ng_ksocket.c | 557 |
1 files changed, 491 insertions, 66 deletions
diff --git a/sys/netgraph/ng_ksocket.c b/sys/netgraph/ng_ksocket.c index 485eeb5..2ce2b4e 100644 --- a/sys/netgraph/ng_ksocket.c +++ b/sys/netgraph/ng_ksocket.c @@ -52,20 +52,26 @@ #include <sys/mbuf.h> #include <sys/proc.h> #include <sys/malloc.h> +#include <sys/ctype.h> #include <sys/protosw.h> #include <sys/errno.h> #include <sys/socket.h> #include <sys/socketvar.h> #include <sys/syslog.h> #include <sys/uio.h> +#include <sys/un.h> #include <netgraph/ng_message.h> #include <netgraph/netgraph.h> +#include <netgraph/ng_parse.h> #include <netgraph/ng_ksocket.h> #include <netinet/in.h> #include <netatalk/at.h> +#define OFFSETOF(s, e) ((char *)&((s *)0)->e - (char *)((s *)0)) +#define SADATA_OFFSET (OFFSETOF(struct sockaddr, sa_data)) + /* Node private data */ struct ng_ksocket_private { hook_p hook; @@ -88,28 +94,6 @@ struct ng_ksocket_alias { const int family; }; -/* Helper functions */ -static void ng_ksocket_incoming(struct socket *so, void *arg, int waitflag); -static int ng_ksocket_parse(const struct ng_ksocket_alias *aliases, - const char *s, int family); - -/* Node type descriptor */ -static struct ng_type ng_ksocket_typestruct = { - NG_VERSION, - NG_KSOCKET_NODE_TYPE, - NULL, - ng_ksocket_constructor, - ng_ksocket_rcvmsg, - ng_ksocket_rmnode, - ng_ksocket_newhook, - NULL, - NULL, - ng_ksocket_rcvdata, - ng_ksocket_rcvdata, - ng_ksocket_disconnect -}; -NETGRAPH_INIT(ksocket, &ng_ksocket_typestruct); - /* Protocol family aliases */ static const struct ng_ksocket_alias ng_ksocket_families[] = { { "local", PF_LOCAL }, @@ -150,6 +134,350 @@ static const struct ng_ksocket_alias ng_ksocket_protos[] = { { NULL, -1 }, }; +/* Helper functions */ +static void ng_ksocket_incoming(struct socket *so, void *arg, int waitflag); +static int ng_ksocket_parse(const struct ng_ksocket_alias *aliases, + const char *s, int family); + +/************************************************************************ + STRUCT SOCKADDR PARSE TYPE + ************************************************************************/ + +/* Get the length of the data portion of a generic struct sockaddr */ +static int +ng_parse_generic_sockdata_getLength(const struct ng_parse_type *type, + const u_char *start, const u_char *buf) +{ + const struct sockaddr *sa; + + sa = (const struct sockaddr *)(buf - SADATA_OFFSET); + return sa->sa_len - SADATA_OFFSET; +} + +/* Type for the variable length data portion of a generic struct sockaddr */ +static const struct ng_parse_type ng_ksocket_generic_sockdata_type = { + &ng_parse_bytearray_type, + &ng_parse_generic_sockdata_getLength +}; + +/* Type for a generic struct sockaddr */ +static const struct ng_parse_struct_info ng_parse_generic_sockaddr_type_info = { + { + { "len", &ng_parse_int8_type }, + { "family", &ng_parse_int8_type }, + { "data", &ng_ksocket_generic_sockdata_type }, + { NULL } + } +}; +static const struct ng_parse_type ng_ksocket_generic_sockaddr_type = { + &ng_parse_struct_type, + &ng_parse_generic_sockaddr_type_info +}; + +/* Convert a struct sockaddr from ASCII to binary. If its a protocol + family that we specially handle, do that, otherwise defer to the + generic parse type ng_ksocket_generic_sockaddr_type. */ +static int +ng_ksocket_sockaddr_parse(const struct ng_parse_type *type, + const char *s, int *off, const u_char *const start, + u_char *const buf, int *buflen) +{ + struct sockaddr *const sa = (struct sockaddr *)buf; + enum ng_parse_token tok; + char fambuf[32]; + int family, len; + char *t; + + /* If next token is a left curly brace, use generic parse type */ + if ((tok = ng_parse_get_token(s, off, &len)) == T_LBRACE) { + return (*ng_ksocket_generic_sockaddr_type.supertype->parse) + (&ng_ksocket_generic_sockaddr_type, + s, off, start, buf, buflen); + } + + /* Get socket address family followed by a slash */ + while (isspace(s[*off])) + (*off)++; + if ((t = index(s + *off, '/')) == NULL) + return (EINVAL); + if ((len = t - (s + *off)) > sizeof(fambuf) - 1) + return (EINVAL); + strncpy(fambuf, s + *off, len); + fambuf[len] = '\0'; + *off += len + 1; + if ((family = ng_ksocket_parse(ng_ksocket_families, fambuf, 0)) == -1) + return (EINVAL); + + /* Set family */ + if (*buflen < SADATA_OFFSET) + return (ERANGE); + sa->sa_family = family; + + /* Set family-specific data and length */ + switch (sa->sa_family) { + case PF_LOCAL: /* Get pathname */ + { + const int pathoff = OFFSETOF(struct sockaddr_un, sun_path); + struct sockaddr_un *const sun = (struct sockaddr_un *)sa; + int toklen, pathlen; + char *path; + + if ((path = ng_get_string_token(s, off, &toklen)) == NULL) + return (EINVAL); + pathlen = strlen(path); + if (pathlen > SOCK_MAXADDRLEN) { + FREE(path, M_NETGRAPH); + return (E2BIG); + } + if (*buflen < pathoff + pathlen) { + FREE(path, M_NETGRAPH); + return (ERANGE); + } + *off += toklen; + bcopy(path, sun->sun_path, pathlen); + sun->sun_len = pathoff + pathlen; + FREE(path, M_NETGRAPH); + break; + } + + case PF_INET: /* Get an IP address with optional port */ + { + struct sockaddr_in *const sin = (struct sockaddr_in *)sa; + int i; + + /* Parse this: <ipaddress>[:port] */ + for (i = 0; i < 4; i++) { + u_long val; + char *eptr; + + val = strtoul(s + *off, &eptr, 10); + if (val > 0xff || eptr == s + *off) + return (EINVAL); + *off += (eptr - (s + *off)); + ((u_char *)&sin->sin_addr)[i] = (u_char)val; + if (i < 3) { + if (s[*off] != '.') + return (EINVAL); + (*off)++; + } else if (s[*off] == ':') { + (*off)++; + val = strtoul(s + *off, &eptr, 10); + if (val > 0xffff || eptr == s + *off) + return (EINVAL); + *off += (eptr - (s + *off)); + sin->sin_port = htons(val); + } else + sin->sin_port = 0; + } + bzero(&sin->sin_zero, sizeof(sin->sin_zero)); + sin->sin_len = sizeof(*sin); + break; + } + +#if 0 + case PF_APPLETALK: /* XXX implement these someday */ + case PF_INET6: + case PF_IPX: +#endif + + default: + return (EINVAL); + } + + /* Done */ + *buflen = sa->sa_len; + return (0); +} + +/* Convert a struct sockaddr from binary to ASCII */ +static int +ng_ksocket_sockaddr_unparse(const struct ng_parse_type *type, + const u_char *data, int *off, char *cbuf, int cbuflen) +{ + const struct sockaddr *sa = (const struct sockaddr *)(data + *off); + int slen = 0; + + /* Output socket address, either in special or generic format */ + switch (sa->sa_family) { + case PF_LOCAL: + { + const int pathoff = OFFSETOF(struct sockaddr_un, sun_path); + const struct sockaddr_un *sun = (const struct sockaddr_un *)sa; + const int pathlen = sun->sun_len - pathoff; + char pathbuf[SOCK_MAXADDRLEN + 1]; + char *pathtoken; + + bcopy(sun->sun_path, pathbuf, pathlen); + pathbuf[pathlen] = '\0'; + if ((pathtoken = ng_encode_string(pathbuf)) == NULL) + return (ENOMEM); + slen += snprintf(cbuf, cbuflen, "local/%s", pathtoken); + FREE(pathtoken, M_NETGRAPH); + if (slen >= cbuflen) + return (ERANGE); + *off += sun->sun_len; + return (0); + } + + case PF_INET: + { + const struct sockaddr_in *sin = (const struct sockaddr_in *)sa; + + slen += snprintf(cbuf, cbuflen, "inet/%d.%d.%d.%d", + ((const u_char *)&sin->sin_addr)[0], + ((const u_char *)&sin->sin_addr)[1], + ((const u_char *)&sin->sin_addr)[2], + ((const u_char *)&sin->sin_addr)[3]); + if (sin->sin_port != 0) { + slen += snprintf(cbuf + strlen(cbuf), + cbuflen - strlen(cbuf), ":%d", + (u_int)ntohs(sin->sin_port)); + } + if (slen >= cbuflen) + return (ERANGE); + *off += sizeof(*sin); + return(0); + } + +#if 0 + case PF_APPLETALK: /* XXX implement these someday */ + case PF_INET6: + case PF_IPX: +#endif + + default: + return (*ng_ksocket_generic_sockaddr_type.supertype->unparse) + (&ng_ksocket_generic_sockaddr_type, + data, off, cbuf, cbuflen); + } +} + +/* Parse type for struct sockaddr */ +static const struct ng_parse_type ng_ksocket_sockaddr_type = { + NULL, + NULL, + NULL, + &ng_ksocket_sockaddr_parse, + &ng_ksocket_sockaddr_unparse, + NULL /* no such thing as a default struct sockaddr */ +}; + +/************************************************************************ + STRUCT NG_KSOCKET_SOCKOPT PARSE TYPE + ************************************************************************/ + +/* Get length of the struct ng_ksocket_sockopt value field, which is the + just the excess of the message argument portion over the length of + the struct ng_ksocket_sockopt. */ +static int +ng_parse_sockoptval_getLength(const struct ng_parse_type *type, + const u_char *start, const u_char *buf) +{ + static const int offset = OFFSETOF(struct ng_ksocket_sockopt, value); + const struct ng_ksocket_sockopt *sopt; + const struct ng_mesg *msg; + + sopt = (const struct ng_ksocket_sockopt *)(buf - offset); + msg = (const struct ng_mesg *)((const u_char *)sopt - sizeof(*msg)); + return msg->header.arglen - sizeof(*sopt); +} + +/* Parse type for the option value part of a struct ng_ksocket_sockopt + XXX Eventually, we should handle the different socket options specially. + XXX This would avoid byte order problems, eg an integer value of 1 is + XXX going to be "[1]" for little endian or "[3=1]" for big endian. */ +static const struct ng_parse_type ng_ksocket_sockoptval_type = { + &ng_parse_bytearray_type, + &ng_parse_sockoptval_getLength +}; + +/* Parse type for struct ng_ksocket_sockopt */ +static const struct ng_parse_struct_info ng_ksocket_sockopt_type_info + = NG_KSOCKET_SOCKOPT_INFO(&ng_ksocket_sockoptval_type); +static const struct ng_parse_type ng_ksocket_sockopt_type = { + &ng_parse_struct_type, + &ng_ksocket_sockopt_type_info, +}; + +/* List of commands and how to convert arguments to/from ASCII */ +static const struct ng_cmdlist ng_ksocket_cmds[] = { + { + NGM_KSOCKET_COOKIE, + NGM_KSOCKET_BIND, + "bind", + &ng_ksocket_sockaddr_type, + NULL + }, + { + NGM_KSOCKET_COOKIE, + NGM_KSOCKET_LISTEN, + "listen", + &ng_parse_int32_type, + NULL + }, + { + NGM_KSOCKET_COOKIE, + NGM_KSOCKET_ACCEPT, + "accept", + NULL, + &ng_ksocket_sockaddr_type + }, + { + NGM_KSOCKET_COOKIE, + NGM_KSOCKET_CONNECT, + "connect", + &ng_ksocket_sockaddr_type, + NULL + }, + { + NGM_KSOCKET_COOKIE, + NGM_KSOCKET_GETNAME, + "getname", + NULL, + &ng_ksocket_sockaddr_type + }, + { + NGM_KSOCKET_COOKIE, + NGM_KSOCKET_GETPEERNAME, + "getpeername", + NULL, + &ng_ksocket_sockaddr_type + }, + { + NGM_KSOCKET_COOKIE, + NGM_KSOCKET_SETOPT, + "setopt", + &ng_ksocket_sockopt_type, + NULL + }, + { + NGM_KSOCKET_COOKIE, + NGM_KSOCKET_GETOPT, + "getopt", + &ng_ksocket_sockopt_type, + &ng_ksocket_sockopt_type + }, + { 0 } +}; + +/* Node type descriptor */ +static struct ng_type ng_ksocket_typestruct = { + NG_VERSION, + NG_KSOCKET_NODE_TYPE, + NULL, + ng_ksocket_constructor, + ng_ksocket_rcvmsg, + ng_ksocket_rmnode, + ng_ksocket_newhook, + NULL, + NULL, + ng_ksocket_rcvdata, + ng_ksocket_rcvdata, + ng_ksocket_disconnect, + ng_ksocket_cmds +}; +NETGRAPH_INIT(ksocket, &ng_ksocket_typestruct); + #define ERROUT(x) do { error = (x); goto done; } while (0) /************************************************************************ @@ -193,10 +521,10 @@ ng_ksocket_constructor(node_p *nodep) static int ng_ksocket_newhook(node_p node, hook_p hook, const char *name0) { + struct proc *p = curproc ? curproc : &proc0; /* XXX broken */ const priv_p priv = node->private; char *s1, *s2, name[NG_HOOKLEN+1]; int family, type, protocol, error; - struct proc *p = &proc0; /* XXX help what to do here */ /* Check if we're already connected */ if (priv->hook != NULL) @@ -243,9 +571,10 @@ static int ng_ksocket_rcvmsg(node_p node, struct ng_mesg *msg, const char *raddr, struct ng_mesg **rptr) { + struct proc *p = curproc ? curproc : &proc0; /* XXX broken */ const priv_p priv = node->private; + struct socket *const so = priv->so; struct ng_mesg *resp = NULL; - struct proc *p = &proc0; int error = 0; switch (msg->header.typecookie) { @@ -253,62 +582,68 @@ ng_ksocket_rcvmsg(node_p node, struct ng_mesg *msg, switch (msg->header.cmd) { case NGM_KSOCKET_BIND: { - struct sockaddr *sa = (struct sockaddr *)msg->data; - struct socket *const so = priv->so; + struct sockaddr *const sa + = (struct sockaddr *)msg->data; - /* Must have a connected hook first */ - if (priv->hook == NULL) - ERROUT(ENETDOWN); + /* Sanity check */ + if (msg->header.arglen < SADATA_OFFSET + || msg->header.arglen < sa->sa_len) + ERROUT(EINVAL); + if (so == NULL) + ERROUT(ENXIO); - /* Set and sanity check sockaddr length */ - if (msg->header.arglen > SOCK_MAXADDRLEN) - ERROUT(ENAMETOOLONG); - sa->sa_len = msg->header.arglen; + /* Bind */ error = sobind(so, sa, p); break; } case NGM_KSOCKET_LISTEN: { - struct socket *const so = priv->so; - int backlog; - - /* Must have a connected hook first */ - if (priv->hook == NULL) - ERROUT(ENETDOWN); - - /* Get backlog argument */ + /* Sanity check */ if (msg->header.arglen != sizeof(int)) ERROUT(EINVAL); - backlog = *((int *)msg->data); + if (so == NULL) + ERROUT(ENXIO); - /* Do listen */ - if ((error = solisten(so, backlog, p)) != 0) + /* Listen */ + if ((error = solisten(so, *((int *)msg->data), p)) != 0) break; /* Notify sender when we get a connection attempt */ /* XXX implement me */ + error = ENODEV; break; } case NGM_KSOCKET_ACCEPT: { - ERROUT(ENODEV); /* XXX implement me */ + /* Sanity check */ + if (msg->header.arglen != 0) + ERROUT(EINVAL); + if (so == NULL) + ERROUT(ENXIO); + + /* Accept on the socket in a non-blocking way */ + /* Create a new ksocket node for the new connection */ + /* Return a response with the peer's sockaddr and + the absolute name of the newly created node */ + + /* XXX implement me */ + + error = ENODEV; break; } case NGM_KSOCKET_CONNECT: { - struct socket *const so = priv->so; - struct sockaddr *sa = (struct sockaddr *)msg->data; - - /* Must have a connected hook first */ - if (priv->hook == NULL) - ERROUT(ENETDOWN); + struct sockaddr *const sa + = (struct sockaddr *)msg->data; - /* Set and sanity check sockaddr length */ - if (msg->header.arglen > SOCK_MAXADDRLEN) - ERROUT(ENAMETOOLONG); - sa->sa_len = msg->header.arglen; + /* Sanity check */ + if (msg->header.arglen < SADATA_OFFSET + || msg->header.arglen < sa->sa_len) + ERROUT(EINVAL); + if (so == NULL) + ERROUT(ENXIO); /* Do connect */ if ((so->so_state & SS_ISCONNECTING) != 0) @@ -325,26 +660,105 @@ ng_ksocket_rcvmsg(node_p node, struct ng_mesg *msg, } case NGM_KSOCKET_GETNAME: - { - ERROUT(ENODEV); /* XXX implement me */ - break; - } - case NGM_KSOCKET_GETPEERNAME: { - ERROUT(ENODEV); /* XXX implement me */ + int (*func)(struct socket *so, struct sockaddr **nam); + struct sockaddr *sa = NULL; + int len; + + /* Sanity check */ + if (msg->header.arglen != 0) + ERROUT(EINVAL); + if (so == NULL) + ERROUT(ENXIO); + + /* Get function */ + if (msg->header.cmd == NGM_KSOCKET_GETPEERNAME) { + if ((so->so_state + & (SS_ISCONNECTED|SS_ISCONFIRMING)) == 0) + ERROUT(ENOTCONN); + func = so->so_proto->pr_usrreqs->pru_peeraddr; + } else + func = so->so_proto->pr_usrreqs->pru_sockaddr; + + /* Get local or peer address */ + if ((error = (*func)(so, &sa)) != 0) + goto bail; + len = (sa == NULL) ? 0 : sa->sa_len; + + /* Send it back in a response */ + NG_MKRESPONSE(resp, msg, len, M_NOWAIT); + if (resp == NULL) { + error = ENOMEM; + goto bail; + } + bcopy(sa, resp->data, len); + + bail: + /* Cleanup */ + if (sa != NULL) + FREE(sa, M_SONAME); break; } case NGM_KSOCKET_GETOPT: { - ERROUT(ENODEV); /* XXX implement me */ + struct ng_ksocket_sockopt *ksopt = + (struct ng_ksocket_sockopt *)msg->data; + struct sockopt sopt; + + /* Sanity check */ + if (msg->header.arglen != sizeof(*ksopt)) + ERROUT(EINVAL); + if (so == NULL) + ERROUT(ENXIO); + + /* Get response with room for option value */ + NG_MKRESPONSE(resp, msg, sizeof(*ksopt) + + NG_KSOCKET_MAX_OPTLEN, M_NOWAIT); + if (resp == NULL) + ERROUT(ENOMEM); + + /* Get socket option, and put value in the response */ + sopt.sopt_dir = SOPT_GET; + sopt.sopt_level = ksopt->level; + sopt.sopt_name = ksopt->name; + sopt.sopt_p = p; + sopt.sopt_valsize = NG_KSOCKET_MAX_OPTLEN; + ksopt = (struct ng_ksocket_sockopt *)resp->data; + sopt.sopt_val = ksopt->value; + if ((error = sogetopt(so, &sopt)) != 0) { + FREE(resp, M_NETGRAPH); + break; + } + + /* Set actual value length */ + resp->header.arglen = sizeof(*ksopt) + + sopt.sopt_valsize; break; } case NGM_KSOCKET_SETOPT: { - ERROUT(ENODEV); /* XXX implement me */ + struct ng_ksocket_sockopt *const ksopt = + (struct ng_ksocket_sockopt *)msg->data; + const int valsize = msg->header.arglen - sizeof(*ksopt); + struct sockopt sopt; + + /* Sanity check */ + if (valsize < 0) + ERROUT(EINVAL); + if (so == NULL) + ERROUT(ENXIO); + + /* Set socket option */ + sopt.sopt_dir = SOPT_SET; + sopt.sopt_level = ksopt->level; + sopt.sopt_name = ksopt->name; + sopt.sopt_val = ksopt->value; + sopt.sopt_valsize = valsize; + sopt.sopt_p = p; + error = sosetopt(so, &sopt); break; } @@ -373,10 +787,10 @@ done: static int ng_ksocket_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) { + struct proc *p = curproc ? curproc : &proc0; /* XXX broken */ const node_p node = hook->node; const priv_p priv = node->private; struct socket *const so = priv->so; - struct proc *p = &proc0; int error; NG_FREE_META(meta); @@ -394,6 +808,9 @@ ng_ksocket_rmnode(node_p node) /* Close our socket (if any) */ if (priv->so != NULL) { + + priv->so->so_upcall = NULL; + priv->so->so_rcv.sb_flags &= ~SB_UPCALL; soclose(priv->so); priv->so = NULL; } @@ -455,8 +872,16 @@ ng_ksocket_incoming(struct socket *so, void *arg, int waitflag) flags = MSG_DONTWAIT; do { if ((error = (*so->so_proto->pr_usrreqs->pru_soreceive) - (so, &nam, &auio, &m, (struct mbuf **)0, &flags)) == 0) + (so, &nam, &auio, &m, (struct mbuf **)0, &flags)) == 0 + && m != NULL) { + struct mbuf *n; + + /* Don't trust the various socket layers to get the + packet header and length correct (eg. kern/15175) */ + for (n = m, m->m_pkthdr.len = 0; n; n = n->m_next) + m->m_pkthdr.len += n->m_len; NG_SEND_DATA(error, priv->hook, m, meta); + } } while (error == 0 && m != NULL); splx(s); } @@ -480,7 +905,7 @@ ng_ksocket_parse(const struct ng_ksocket_alias *aliases, /* Try parsing as a number */ val = (int)strtoul(s, &eptr, 10); - if (val <= 0 || *eptr != '\0') + if (val < 0 || *eptr != '\0') return (-1); return (val); } |