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
util_timeval_to_ticks(const struct timeval * tv,clock_t * ticksp)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
util_ticks_to_timeval(clock_t ticks,struct timeval * tv)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
util_copy_data(const struct sockdriver_data * data,size_t len,size_t off,const struct pbuf * pbuf,size_t skip,int copy_in)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
util_coalesce(char * ptr,size_t max,const iovec_t * iov,unsigned int iovcnt)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
util_is_root(endpoint_t endpt)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
util_convert_err(err_t err)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
util_pcblist(struct rmib_call * call,struct rmib_oldp * oldp,const void * (* enum_proc)(const void *),void (* get_info_proc)(struct kinfo_pcb *,const void *))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