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