xref: /freebsd/lib/libnetgraph/sock.c (revision cc68614da8232d8baaca0ae0d0dd8f890f06623e)
1 /*
2  * sock.c
3  *
4  * Copyright (c) 1996-1999 Whistle Communications, Inc.
5  * All rights reserved.
6  *
7  * Subject to the following obligations and disclaimer of warranty, use and
8  * redistribution of this software, in source or object code forms, with or
9  * without modifications are expressly permitted by Whistle Communications;
10  * provided, however, that:
11  * 1. Any and all reproductions of the source or object code must include the
12  *    copyright notice above and the following disclaimer of warranties; and
13  * 2. No rights are granted, in any manner or form, to use Whistle
14  *    Communications, Inc. trademarks, including the mark "WHISTLE
15  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
16  *    such appears in the above copyright notice or in the software.
17  *
18  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
19  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
20  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
21  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
23  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
24  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
25  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
26  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
27  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
28  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
29  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
30  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
31  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
34  * OF SUCH DAMAGE.
35  *
36  * Author: Archie Cobbs <archie@whistle.com>
37  *
38  * $Whistle: sock.c,v 1.12 1999/01/20 00:57:23 archie Exp $
39  */
40 
41 #include <sys/cdefs.h>
42 __FBSDID("$FreeBSD$");
43 
44 #include <sys/types.h>
45 #include <sys/socket.h>
46 #include <stdarg.h>
47 #include <netgraph/ng_message.h>
48 #include <netgraph/ng_socket.h>
49 
50 #include "netgraph.h"
51 #include "internal.h"
52 
53 /* The socket node type KLD */
54 #define NG_SOCKET_KLD	"ng_socket.ko"
55 
56 /*
57  * Create a socket type node and give it the supplied name.
58  * Return data and control sockets corresponding to the node.
59  * Returns -1 if error and sets errno.
60  */
61 int
62 NgMkSockNode(const char *name, int *csp, int *dsp)
63 {
64 	char namebuf[NG_NODESIZ];
65 	int cs = -1;		/* control socket */
66 	int ds = -1;		/* data socket */
67 	int errnosv;
68 
69 	/* Empty name means no name */
70 	if (name && *name == 0)
71 		name = NULL;
72 
73 	/* Create control socket; this also creates the netgraph node.
74 	   If we get an EAFNOSUPPORT then the socket node type is
75 	   not loaded, so load it and try again. */
76 	if ((cs = socket(AF_NETGRAPH, SOCK_DGRAM, NG_CONTROL)) < 0) {
77 		if (errno == EAFNOSUPPORT) {
78 			if (kldload(NG_SOCKET_KLD) < 0) {
79 				errnosv = errno;
80 				if (_gNgDebugLevel >= 1)
81 					NGLOG("can't load %s", NG_SOCKET_KLD);
82 				goto errout;
83 			}
84 			cs = socket(AF_NETGRAPH, SOCK_DGRAM, NG_CONTROL);
85 			if (cs >= 0)
86 				goto gotNode;
87 		}
88 		errnosv = errno;
89 		if (_gNgDebugLevel >= 1)
90 			NGLOG("socket");
91 		goto errout;
92 	}
93 
94 gotNode:
95 	/* Assign the node the desired name, if any */
96 	if (name != NULL) {
97 		u_char sbuf[NG_NODESIZ + NGSA_OVERHEAD];
98 		struct sockaddr_ng *const sg = (struct sockaddr_ng *) sbuf;
99 
100 		/* Assign name */
101 		strlcpy(sg->sg_data, name, NG_NODESIZ);
102 		sg->sg_family = AF_NETGRAPH;
103 		sg->sg_len = strlen(sg->sg_data) + 1 + NGSA_OVERHEAD;
104 		if (bind(cs, (struct sockaddr *) sg, sg->sg_len) < 0) {
105 			errnosv = errno;
106 			if (_gNgDebugLevel >= 1)
107 				NGLOG("bind(%s)", sg->sg_data);
108 			goto errout;
109 		}
110 
111 		/* Save node name */
112 		strlcpy(namebuf, name, sizeof(namebuf));
113 	} else if (dsp != NULL) {
114 		union {
115 			u_char rbuf[sizeof(struct ng_mesg) +
116 			    sizeof(struct nodeinfo)];
117 			struct ng_mesg res;
118 		} res;
119 		struct nodeinfo *const ni = (struct nodeinfo *) res.res.data;
120 
121 		/* Find out the node ID */
122 		if (NgSendMsg(cs, ".", NGM_GENERIC_COOKIE,
123 		    NGM_NODEINFO, NULL, 0) < 0) {
124 			errnosv = errno;
125 			if (_gNgDebugLevel >= 1)
126 				NGLOG("send nodeinfo");
127 			goto errout;
128 		}
129 		if (NgRecvMsg(cs, &res.res, sizeof(res.rbuf), NULL) < 0) {
130 			errnosv = errno;
131 			if (_gNgDebugLevel >= 1)
132 				NGLOG("recv nodeinfo");
133 			goto errout;
134 		}
135 
136 		/* Save node "name" */
137 		snprintf(namebuf, sizeof(namebuf), "[%lx]", (u_long) ni->id);
138 	}
139 
140 	/* Create data socket if desired */
141 	if (dsp != NULL) {
142 		u_char sbuf[NG_NODESIZ + 1 + NGSA_OVERHEAD];
143 		struct sockaddr_ng *const sg = (struct sockaddr_ng *) sbuf;
144 
145 		/* Create data socket, initially just "floating" */
146 		if ((ds = socket(AF_NETGRAPH, SOCK_DGRAM, NG_DATA)) < 0) {
147 			errnosv = errno;
148 			if (_gNgDebugLevel >= 1)
149 				NGLOG("socket");
150 			goto errout;
151 		}
152 
153 		/* Associate the data socket with the node */
154 		snprintf(sg->sg_data, NG_NODESIZ + 1, "%s:", namebuf);
155 		sg->sg_family = AF_NETGRAPH;
156 		sg->sg_len = strlen(sg->sg_data) + 1 + NGSA_OVERHEAD;
157 		if (connect(ds, (struct sockaddr *) sg, sg->sg_len) < 0) {
158 			errnosv = errno;
159 			if (_gNgDebugLevel >= 1)
160 				NGLOG("connect(%s)", sg->sg_data);
161 			goto errout;
162 		}
163 	}
164 
165 	/* Return the socket(s) */
166 	if (csp)
167 		*csp = cs;
168 	else
169 		close(cs);
170 	if (dsp)
171 		*dsp = ds;
172 	return (0);
173 
174 errout:
175 	/* Failed */
176 	if (cs >= 0)
177 		close(cs);
178 	if (ds >= 0)
179 		close(ds);
180 	errno = errnosv;
181 	return (-1);
182 }
183 
184 /*
185  * Assign a globally unique name to a node
186  * Returns -1 if error and sets errno.
187  */
188 int
189 NgNameNode(int cs, const char *path, const char *fmt, ...)
190 {
191 	struct ngm_name ngn;
192 	va_list args;
193 
194 	/* Build message arg */
195 	va_start(args, fmt);
196 	vsnprintf(ngn.name, sizeof(ngn.name), fmt, args);
197 	va_end(args);
198 
199 	/* Send message */
200 	if (NgSendMsg(cs, path,
201 	    NGM_GENERIC_COOKIE, NGM_NAME, &ngn, sizeof(ngn)) < 0) {
202 		if (_gNgDebugLevel >= 1)
203 			NGLOGX("%s: failed", __func__);
204 		return (-1);
205 	}
206 
207 	/* Done */
208 	return (0);
209 }
210 
211 /*
212  * Read a packet from a data socket
213  * Returns -1 if error and sets errno.
214  */
215 int
216 NgRecvData(int ds, u_char * buf, size_t len, char *hook)
217 {
218 	u_char frombuf[NG_HOOKSIZ + NGSA_OVERHEAD];
219 	struct sockaddr_ng *const from = (struct sockaddr_ng *) frombuf;
220 	socklen_t fromlen = sizeof(frombuf);
221 	int rtn, errnosv;
222 
223 	/* Read packet */
224 	rtn = recvfrom(ds, buf, len, 0, (struct sockaddr *) from, &fromlen);
225 	if (rtn < 0) {
226 		errnosv = errno;
227 		if (_gNgDebugLevel >= 1)
228 			NGLOG("recvfrom");
229 		errno = errnosv;
230 		return (-1);
231 	}
232 
233 	/* Copy hook name */
234 	if (hook != NULL)
235 		strlcpy(hook, from->sg_data, NG_HOOKSIZ);
236 
237 	/* Debugging */
238 	if (_gNgDebugLevel >= 2) {
239 		NGLOGX("READ %s from hook \"%s\" (%d bytes)",
240 		       rtn ? "PACKET" : "EOF", from->sg_data, rtn);
241 		if (_gNgDebugLevel >= 3)
242 			_NgDebugBytes(buf, rtn);
243 	}
244 
245 	/* Done */
246 	return (rtn);
247 }
248 
249 /*
250  * Identical to NgRecvData() except buffer is dynamically allocated.
251  */
252 int
253 NgAllocRecvData(int ds, u_char **buf, char *hook)
254 {
255 	int len;
256 	socklen_t optlen;
257 
258 	optlen = sizeof(len);
259 	if (getsockopt(ds, SOL_SOCKET, SO_RCVBUF, &len, &optlen) == -1 ||
260 	    (*buf = malloc(len)) == NULL)
261 		return (-1);
262 	if ((len = NgRecvData(ds, *buf, len, hook)) < 0)
263 		free(*buf);
264 	return (len);
265 }
266 
267 /*
268  * Write a packet to a data socket. The packet will be sent
269  * out the corresponding node on the specified hook.
270  * Returns -1 if error and sets errno.
271  */
272 int
273 NgSendData(int ds, const char *hook, const u_char * buf, size_t len)
274 {
275 	u_char sgbuf[NG_HOOKSIZ + NGSA_OVERHEAD];
276 	struct sockaddr_ng *const sg = (struct sockaddr_ng *) sgbuf;
277 	int errnosv;
278 
279 	/* Set up destination hook */
280 	sg->sg_family = AF_NETGRAPH;
281 	strlcpy(sg->sg_data, hook, NG_HOOKSIZ);
282 	sg->sg_len = strlen(sg->sg_data) + 1 + NGSA_OVERHEAD;
283 
284 	/* Debugging */
285 	if (_gNgDebugLevel >= 2) {
286 		NGLOGX("WRITE PACKET to hook \"%s\" (%d bytes)", hook, len);
287 		_NgDebugSockaddr(sg);
288 		if (_gNgDebugLevel >= 3)
289 			_NgDebugBytes(buf, len);
290 	}
291 
292 	/* Send packet */
293 	if (sendto(ds, buf, len, 0, (struct sockaddr *) sg, sg->sg_len) < 0) {
294 		errnosv = errno;
295 		if (_gNgDebugLevel >= 1)
296 			NGLOG("sendto(%s)", sg->sg_data);
297 		errno = errnosv;
298 		return (-1);
299 	}
300 
301 	/* Done */
302 	return (0);
303 }
304 
305