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