xref: /minix/minix/net/lwip/util.c (revision bb9622b5)
1 /* LWIP service - util.c - shared utility functions */
2 
3 #include "lwip.h"
4 
5 #define US	1000000			/* number of microseconds per second */
6 
7 /*
8  * Convert the given timeval structure to a number of clock ticks, checking
9  * whether the given structure is valid and whether the resulting number of
10  * ticks can be expressed as a (relative) clock ticks value.  Upon success,
11  * return OK, with the number of clock ticks stored in 'ticksp'.  Upon failure,
12  * return a negative error code that may be returned to userland directly.  In
13  * that case, the contents of 'ticksp' are left unchanged.
14  *
15  * TODO: move this function into libsys and remove other redundant copies.
16  */
17 int
18 util_timeval_to_ticks(const struct timeval * tv, clock_t * ticksp)
19 {
20 	clock_t ticks;
21 
22 	if (tv->tv_sec < 0 || tv->tv_usec < 0 || tv->tv_usec >= US)
23 		return EINVAL;
24 
25 	if (tv->tv_sec >= TMRDIFF_MAX / sys_hz())
26 		return EDOM;
27 
28 	ticks = tv->tv_sec * sys_hz() + (tv->tv_usec * sys_hz() + US - 1) / US;
29 	assert(ticks <= TMRDIFF_MAX);
30 
31 	*ticksp = ticks;
32 	return OK;
33 }
34 
35 /*
36  * Convert the given number of clock ticks to a timeval structure.  This
37  * function never fails.
38  */
39 void
40 util_ticks_to_timeval(clock_t ticks, struct timeval * tv)
41 {
42 
43 	memset(tv, 0, sizeof(*tv));
44 	tv->tv_sec = ticks / sys_hz();
45 	tv->tv_usec = (ticks % sys_hz()) * US / sys_hz();
46 }
47 
48 /*
49  * Copy data between a user process and a chain of buffers.  If the 'copy_in'
50  * flag is set, the data will be copied in from the user process to the given
51  * chain of buffers; otherwise, the data will be copied out from the given
52  * buffer chain to the user process.  The 'data' parameter is a sockdriver-
53  * supplied structure identifying the remote source or destination of the data.
54  * The 'len' parameter contains the number of bytes to copy, and 'off' contains
55  * the offset into the remote source or destination.  'pbuf' is a pointer to
56  * the buffer chain, and 'skip' is the number of bytes to skip in the first
57  * buffer on the chain.  Return OK on success, or a negative error code if the
58  * copy operation failed.  This function is packet queue friendly.
59  */
60 int
61 util_copy_data(const struct sockdriver_data * data, size_t len, size_t off,
62 	const struct pbuf * pbuf, size_t skip, int copy_in)
63 {
64 	iovec_t iov[SOCKDRIVER_IOV_MAX];
65 	unsigned int i;
66 	size_t sub, chunk;
67 	int r;
68 
69 	while (len > 0) {
70 		sub = 0;
71 
72 		for (i = 0; len > 0 && i < __arraycount(iov); i++) {
73 			assert(pbuf != NULL);
74 
75 			chunk = (size_t)pbuf->len - skip;
76 			if (chunk > len)
77 				chunk = len;
78 
79 			iov[i].iov_addr = (vir_bytes)pbuf->payload + skip;
80 			iov[i].iov_size = chunk;
81 
82 			sub += chunk;
83 			len -= chunk;
84 
85 			pbuf = pbuf->next;
86 			skip = 0;
87 		}
88 
89 		if (copy_in)
90 			r = sockdriver_vcopyin(data, off, iov, i);
91 		else
92 			r = sockdriver_vcopyout(data, off, iov, i);
93 		if (r != OK)
94 			return r;
95 
96 		off += sub;
97 	}
98 
99 	return OK;
100 }
101 
102 /*
103  * Copy from a vector of (local) buffers to a single (local) buffer.  Return
104  * the total number of copied bytes on success, or E2BIG if not all of the
105  * results could be stored in the given bfufer.
106  */
107 ssize_t
108 util_coalesce(char * ptr, size_t max, const iovec_t * iov, unsigned int iovcnt)
109 {
110 	size_t off, size;
111 
112 	for (off = 0; iovcnt > 0; iov++, iovcnt--) {
113 		if ((size = iov->iov_size) > max)
114 			return E2BIG;
115 
116 		memcpy(&ptr[off], (void *)iov->iov_addr, size);
117 
118 		off += size;
119 		max -= size;
120 	}
121 
122 	return off;
123 }
124 
125 /*
126  * Return TRUE if the given endpoint has superuser privileges, FALSE otherwise.
127  */
128 int
129 util_is_root(endpoint_t endpt)
130 {
131 
132 	return (getnuid(endpt) == ROOT_EUID);
133 }
134 
135 /*
136  * Convert a lwIP-provided error code (of type err_t) to a negative MINIX 3
137  * error code.
138  */
139 int
140 util_convert_err(err_t err)
141 {
142 
143 	switch (err) {
144 	case ERR_OK:		return OK;
145 	case ERR_MEM:		return ENOMEM;
146 	case ERR_BUF:		return ENOBUFS;
147 	case ERR_TIMEOUT:	return ETIMEDOUT;
148 	case ERR_RTE:		return EHOSTUNREACH;
149 	case ERR_VAL:		return EINVAL;
150 	case ERR_USE:		return EADDRINUSE;
151 	case ERR_ALREADY:	return EALREADY;
152 	case ERR_ISCONN:	return EISCONN;
153 	case ERR_CONN:		return ENOTCONN;
154 	case ERR_IF:		return ENETDOWN;
155 	case ERR_ABRT:		return ECONNABORTED;
156 	case ERR_RST:		return ECONNRESET;
157 	case ERR_INPROGRESS:	return EINPROGRESS; /* should not be thrown */
158 	case ERR_WOULDBLOCK:	return EWOULDBLOCK; /* should not be thrown */
159 	case ERR_ARG:		return EINVAL;
160 	case ERR_CLSD:		/* should be caught as separate case */
161 	default:		/* should have a case here */
162 		printf("LWIP: unexpected error from lwIP: %d", err);
163 		return EGENERIC;
164 	}
165 }
166 
167 /*
168  * Obtain the list of protocol control blocks for a particular domain and
169  * protocol.  The call may be used for requesting either IPv4 or IPv6 PCBs,
170  * based on the path used to get here.  It is used for TCP, UDP, and RAW PCBs.
171  */
172 ssize_t
173 util_pcblist(struct rmib_call * call, struct rmib_oldp * oldp,
174 	const void *(*enum_proc)(const void *),
175 	void (*get_info_proc)(struct kinfo_pcb *, const void *))
176 {
177 	const void *pcb;
178 	ip_addr_t local_ip;
179 	struct kinfo_pcb ki;
180 	ssize_t off;
181 	int r, size, max, domain, protocol;
182 
183 	if (call->call_namelen != 4)
184 		return EINVAL;
185 
186 	/* The first two added name fields are not used. */
187 
188 	size = call->call_name[2];
189 	if (size < 0 || (size_t)size > sizeof(ki))
190 		return EINVAL;
191 	if (size == 0)
192 		size = sizeof(ki);
193 	max = call->call_name[3];
194 
195 	domain = call->call_oname[1];
196 	protocol = call->call_oname[2];
197 
198 	off = 0;
199 
200 	for (pcb = enum_proc(NULL); pcb != NULL; pcb = enum_proc(pcb)) {
201 		/* Filter on IPv4/IPv6. */
202 		memcpy(&local_ip, &((const struct ip_pcb *)pcb)->local_ip,
203 		    sizeof(local_ip));
204 
205 		/*
206 		 * lwIP does not support IPv6 sockets with IPv4-mapped IPv6
207 		 * addresses, and requires that those be represented as IPv4
208 		 * sockets instead.  We perform the appropriate conversions to
209 		 * make that work in general, but here we only have the lwIP
210 		 * PCB to go on, and that PCB may not even have an associated
211 		 * sock data structure.  As a result, we have to report IPv6
212 		 * sockets with IPv4-mapped IPv6 addresses as IPv4 sockets
213 		 * here.  There is little room for improvement until lwIP
214 		 * allows us to store a "this is really an IPv6 socket" flag in
215 		 * its PCBs.  As documented in the ipsock module, a partial
216 		 * solution would for example cause TCP sockets to "jump" from
217 		 * the IPv6 listing to the IPv4 listing when entering TIME_WAIT
218 		 * state.  The jumping already occurs now for sockets that are
219 		 * getting bound, but that is not as problematic.
220 		 */
221 		if ((domain == AF_INET) != IP_IS_V4(&local_ip))
222 			continue;
223 
224 		if (rmib_inrange(oldp, off)) {
225 			memset(&ki, 0, sizeof(ki));
226 
227 			ki.ki_pcbaddr = (uint64_t)(uintptr_t)pcb;
228 			ki.ki_ppcbaddr = (uint64_t)(uintptr_t)pcb;
229 			ki.ki_family = domain;
230 			ki.ki_protocol = protocol;
231 
232 			get_info_proc(&ki, pcb);
233 
234 			if ((r = rmib_copyout(oldp, off, &ki, size)) < OK)
235 				return r;
236 		}
237 
238 		off += size;
239 		if (max > 0 && --max == 0)
240 			break;
241 	}
242 
243 	/*
244 	 * Margin to limit the possible effects of the inherent race condition
245 	 * between receiving just the data size and receiving the actual data.
246 	 */
247 	if (oldp == NULL)
248 		off += PCB_SLOP * size;
249 
250 	return off;
251 }
252