1 /* 2 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #include <jni.h> 27 #include <string.h> 28 29 #include "net_util.h" 30 #include "jdk_net_SocketFlow.h" 31 32 static jclass sf_status_class; /* Status enum type */ 33 34 static jfieldID sf_status; 35 static jfieldID sf_priority; 36 static jfieldID sf_bandwidth; 37 38 static jfieldID sf_fd_fdID; /* FileDescriptor.fd */ 39 40 /* References to the literal enum values */ 41 42 static jobject sfs_NOSTATUS; 43 static jobject sfs_OK; 44 static jobject sfs_NOPERMISSION; 45 static jobject sfs_NOTCONNECTED; 46 static jobject sfs_NOTSUPPORTED; 47 static jobject sfs_ALREADYCREATED; 48 static jobject sfs_INPROGRESS; 49 static jobject sfs_OTHER; 50 51 static jobject getEnumField(JNIEnv *env, char *name); 52 static void setStatus(JNIEnv *env, jobject obj, int errval); 53 54 /* OS specific code is implemented in these three functions */ 55 56 static jboolean flowSupported0() ; 57 static jboolean reuseportSupported0() ; 58 59 /* 60 * Class: sun_net_ExtendedOptionsImpl 61 * Method: init 62 * Signature: ()V 63 */ 64 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_init 65 (JNIEnv *env, jclass UNUSED) 66 { 67 static int initialized = 0; 68 jclass c; 69 70 /* Global class references */ 71 72 if (initialized) { 73 return; 74 } 75 76 c = (*env)->FindClass(env, "jdk/net/SocketFlow$Status"); 77 CHECK_NULL(c); 78 sf_status_class = (*env)->NewGlobalRef(env, c); 79 CHECK_NULL(sf_status_class); 80 81 /* int "fd" field of java.io.FileDescriptor */ 82 83 c = (*env)->FindClass(env, "java/io/FileDescriptor"); 84 CHECK_NULL(c); 85 sf_fd_fdID = (*env)->GetFieldID(env, c, "fd", "I"); 86 CHECK_NULL(sf_fd_fdID); 87 88 89 /* SocketFlow fields */ 90 91 c = (*env)->FindClass(env, "jdk/net/SocketFlow"); 92 CHECK_NULL(c); 93 94 /* status */ 95 96 sf_status = (*env)->GetFieldID(env, c, "status", 97 "Ljdk/net/SocketFlow$Status;"); 98 CHECK_NULL(sf_status); 99 100 /* priority */ 101 102 sf_priority = (*env)->GetFieldID(env, c, "priority", "I"); 103 CHECK_NULL(sf_priority); 104 105 /* bandwidth */ 106 107 sf_bandwidth = (*env)->GetFieldID(env, c, "bandwidth", "J"); 108 CHECK_NULL(sf_bandwidth); 109 110 /* Initialize the static enum values */ 111 112 sfs_NOSTATUS = getEnumField(env, "NO_STATUS"); 113 CHECK_NULL(sfs_NOSTATUS); 114 sfs_OK = getEnumField(env, "OK"); 115 CHECK_NULL(sfs_OK); 116 sfs_NOPERMISSION = getEnumField(env, "NO_PERMISSION"); 117 CHECK_NULL(sfs_NOPERMISSION); 118 sfs_NOTCONNECTED = getEnumField(env, "NOT_CONNECTED"); 119 CHECK_NULL(sfs_NOTCONNECTED); 120 sfs_NOTSUPPORTED = getEnumField(env, "NOT_SUPPORTED"); 121 CHECK_NULL(sfs_NOTSUPPORTED); 122 sfs_ALREADYCREATED = getEnumField(env, "ALREADY_CREATED"); 123 CHECK_NULL(sfs_ALREADYCREATED); 124 sfs_INPROGRESS = getEnumField(env, "IN_PROGRESS"); 125 CHECK_NULL(sfs_INPROGRESS); 126 sfs_OTHER = getEnumField(env, "OTHER"); 127 CHECK_NULL(sfs_OTHER); 128 initialized = JNI_TRUE; 129 } 130 131 static jobject getEnumField(JNIEnv *env, char *name) 132 { 133 jobject f; 134 jfieldID fID = (*env)->GetStaticFieldID(env, sf_status_class, name, 135 "Ljdk/net/SocketFlow$Status;"); 136 CHECK_NULL_RETURN(fID, NULL); 137 138 f = (*env)->GetStaticObjectField(env, sf_status_class, fID); 139 CHECK_NULL_RETURN(f, NULL); 140 f = (*env)->NewGlobalRef(env, f); 141 CHECK_NULL_RETURN(f, NULL); 142 return f; 143 } 144 145 /* 146 * Returns a java.lang.Boolean based on 'b' 147 */ 148 static jobject createBoolean(JNIEnv *env, int b) { 149 static jclass b_class; 150 static jmethodID b_ctrID; 151 152 if (b_class == NULL) { 153 jclass c = (*env)->FindClass(env, "java/lang/Boolean"); 154 CHECK_NULL_RETURN(c, NULL); 155 b_ctrID = (*env)->GetMethodID(env, c, "<init>", "(Z)V"); 156 CHECK_NULL_RETURN(b_ctrID, NULL); 157 b_class = (*env)->NewGlobalRef(env, c); 158 CHECK_NULL_RETURN(b_class, NULL); 159 } 160 161 return( (*env)->NewObject(env, b_class, b_ctrID, (jboolean)(b!=0)) ); 162 } 163 164 /* 165 * Retrieve the int file-descriptor from a public socket type object. 166 * Gets impl, then the FileDescriptor from the impl, and then the fd 167 * from that. 168 */ 169 static int getFD(JNIEnv *env, jobject fileDesc) { 170 return (*env)->GetIntField(env, fileDesc, sf_fd_fdID); 171 } 172 173 /** 174 * Sets the status field of a SocketFlow to one of the 175 * canned enum values 176 */ 177 static void setStatus (JNIEnv *env, jobject obj, int errval) 178 { 179 switch (errval) { 180 case 0: /* OK */ 181 (*env)->SetObjectField(env, obj, sf_status, sfs_OK); 182 break; 183 case EPERM: 184 (*env)->SetObjectField(env, obj, sf_status, sfs_NOPERMISSION); 185 break; 186 case ENOTCONN: 187 (*env)->SetObjectField(env, obj, sf_status, sfs_NOTCONNECTED); 188 break; 189 case EOPNOTSUPP: 190 (*env)->SetObjectField(env, obj, sf_status, sfs_NOTSUPPORTED); 191 break; 192 case EALREADY: 193 (*env)->SetObjectField(env, obj, sf_status, sfs_ALREADYCREATED); 194 break; 195 case EINPROGRESS: 196 (*env)->SetObjectField(env, obj, sf_status, sfs_INPROGRESS); 197 break; 198 default: 199 (*env)->SetObjectField(env, obj, sf_status, sfs_OTHER); 200 break; 201 } 202 } 203 204 #ifdef __solaris__ 205 206 /* 207 * Class: sun_net_ExtendedOptionsImpl 208 * Method: setFlowOption 209 * Signature: (Ljava/io/FileDescriptor;Ljdk/net/SocketFlow;)V 210 */ 211 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setFlowOption 212 (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) 213 { 214 int fd = getFD(env, fileDesc); 215 216 if (fd < 0) { 217 NET_ERROR(env, JNU_JAVANETPKG "SocketException", "socket closed"); 218 return; 219 } else { 220 sock_flow_props_t props; 221 jlong bandwidth; 222 int rv; 223 224 jint priority = (*env)->GetIntField(env, flow, sf_priority); 225 memset(&props, 0, sizeof(props)); 226 props.sfp_version = SOCK_FLOW_PROP_VERSION1; 227 228 if (priority != jdk_net_SocketFlow_UNSET) { 229 props.sfp_mask |= SFP_PRIORITY; 230 props.sfp_priority = priority; 231 } 232 bandwidth = (*env)->GetLongField(env, flow, sf_bandwidth); 233 if (bandwidth > -1) { 234 props.sfp_mask |= SFP_MAXBW; 235 props.sfp_maxbw = (uint64_t) bandwidth; 236 } 237 rv = setsockopt(fd, SOL_SOCKET, SO_FLOW_SLA, &props, sizeof(props)); 238 if (rv < 0) { 239 if (errno == ENOPROTOOPT) { 240 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 241 "unsupported socket option"); 242 } else if (errno == EACCES || errno == EPERM) { 243 NET_ERROR(env, JNU_JAVANETPKG "SocketException", 244 "Permission denied"); 245 } else { 246 NET_ERROR(env, JNU_JAVANETPKG "SocketException", 247 "set option SO_FLOW_SLA failed"); 248 } 249 return; 250 } 251 setStatus(env, flow, props.sfp_status); 252 } 253 } 254 255 /* 256 * Class: sun_net_ExtendedOptionsImpl 257 * Method: getFlowOption 258 * Signature: (Ljava/io/FileDescriptor;Ljdk/net/SocketFlow;)V 259 */ 260 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_getFlowOption 261 (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) 262 { 263 int fd = getFD(env, fileDesc); 264 265 if (fd < 0) { 266 NET_ERROR(env, JNU_JAVANETPKG "SocketException", "socket closed"); 267 return; 268 } else { 269 sock_flow_props_t props; 270 int status; 271 socklen_t sz = sizeof(props); 272 273 int rv = getsockopt(fd, SOL_SOCKET, SO_FLOW_SLA, &props, &sz); 274 if (rv < 0) { 275 if (errno == ENOPROTOOPT) { 276 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 277 "unsupported socket option"); 278 } else if (errno == EACCES || errno == EPERM) { 279 NET_ERROR(env, JNU_JAVANETPKG "SocketException", 280 "Permission denied"); 281 } else { 282 NET_ERROR(env, JNU_JAVANETPKG "SocketException", 283 "set option SO_FLOW_SLA failed"); 284 } 285 return; 286 } 287 /* first check status to see if flow exists */ 288 status = props.sfp_status; 289 setStatus(env, flow, status); 290 if (status == 0) { /* OK */ 291 /* can set the other fields now */ 292 if (props.sfp_mask & SFP_PRIORITY) { 293 (*env)->SetIntField(env, flow, sf_priority, props.sfp_priority); 294 } 295 if (props.sfp_mask & SFP_MAXBW) { 296 (*env)->SetLongField(env, flow, sf_bandwidth, 297 (jlong)props.sfp_maxbw); 298 } 299 } 300 } 301 } 302 303 static jboolean flowsupported; 304 static jboolean flowsupported_set = JNI_FALSE; 305 306 static jboolean flowSupported0() 307 { 308 /* Do a simple dummy call, and try to figure out from that */ 309 sock_flow_props_t props; 310 int rv, s; 311 if (flowsupported_set) { 312 return flowsupported; 313 } 314 s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 315 if (s < 0) { 316 flowsupported = JNI_FALSE; 317 flowsupported_set = JNI_TRUE; 318 return JNI_FALSE; 319 } 320 memset(&props, 0, sizeof(props)); 321 props.sfp_version = SOCK_FLOW_PROP_VERSION1; 322 props.sfp_mask |= SFP_PRIORITY; 323 props.sfp_priority = SFP_PRIO_NORMAL; 324 rv = setsockopt(s, SOL_SOCKET, SO_FLOW_SLA, &props, sizeof(props)); 325 if (rv != 0 && errno == ENOPROTOOPT) { 326 rv = JNI_FALSE; 327 } else { 328 rv = JNI_TRUE; 329 } 330 close(s); 331 flowsupported = rv; 332 flowsupported_set = JNI_TRUE; 333 return flowsupported; 334 } 335 336 #else /* __solaris__ */ 337 338 /* Non Solaris. Functionality is not supported. So, throw UnsupportedOpExc */ 339 340 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setFlowOption 341 (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) 342 { 343 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 344 "unsupported socket option"); 345 } 346 347 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_getFlowOption 348 (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) 349 { 350 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 351 "unsupported socket option"); 352 } 353 354 static jboolean flowSupported0() { 355 return JNI_FALSE; 356 } 357 358 #endif /* __solaris__ */ 359 360 361 #ifdef __linux__ 362 /* 363 * Class: sun_net_ExtendedOptionsImpl 364 * Method: setReusePortOption 365 * Signature: (Ljava/io/FileDescriptor;Ljava/lang/Object;)V 366 */ 367 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setReusePortOption 368 (JNIEnv *env, jclass UNUSED, jobject fileDesc, jboolean on) 369 { 370 int fd = getFD(env, fileDesc); 371 int optval; 372 373 if (fd < 0) { 374 NET_ERROR(env, JNU_JAVANETPKG "SocketException", "socket closed"); 375 return; 376 } else { 377 int rv; 378 optval = (on ? 1 : 0); 379 rv = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); 380 if (rv < 0) { 381 if (errno == ENOPROTOOPT) { 382 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 383 "unsupported socket option"); 384 } else { 385 NET_ERROR(env, JNU_JAVANETPKG "SocketException", 386 "set option SO_REUSEPORT failed"); 387 } 388 return; 389 } 390 } 391 } 392 /* 393 * Class: sun_net_ExtendedOptionsImpl 394 * Method: getReusePortOption 395 * Signature: (Ljava/io/FileDescriptor;)Ljava/lang/Object; 396 */ 397 JNIEXPORT jobject JNICALL Java_sun_net_ExtendedOptionsImpl_getReusePortOption 398 (JNIEnv *env, jclass UNUSED, jobject fileDesc) 399 { 400 int fd = getFD(env, fileDesc); 401 402 if (fd < 0) { 403 NET_ERROR(env, JNU_JAVANETPKG "SocketException", "socket closed"); 404 return JNI_FALSE; 405 } else { 406 int on; 407 socklen_t sz = sizeof(on); 408 409 int rv = getsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, &sz); 410 if (rv < 0) { 411 if (errno == ENOPROTOOPT) { 412 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 413 "unsupported socket option"); 414 } else { 415 NET_ERROR(env, JNU_JAVANETPKG "SocketException", 416 "set option SO_REUSEPORT failed"); 417 } 418 } 419 printf("getReusePortOption returns %d\n", on); 420 return createBoolean(env, on); 421 } 422 } 423 424 static jboolean reuseportsupported; 425 static jboolean reuseportsupported_set = JNI_FALSE; 426 427 static jboolean reuseportSupported0() 428 { 429 /* Do a simple dummy call, and try to figure out from that */ 430 int one = 1; 431 int rv, s; 432 if (reuseportsupported_set) { 433 return reuseportsupported; 434 } 435 s = socket(PF_INET, SOCK_STREAM, 0); 436 if (s < 0) { 437 reuseportsupported = JNI_FALSE; 438 reuseportsupported_set = JNI_TRUE; 439 return JNI_FALSE; 440 } 441 rv = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (void *)&one, sizeof(one)); 442 if (rv != 0 && errno == ENOPROTOOPT) { 443 rv = JNI_FALSE; 444 } else { 445 rv = JNI_TRUE; 446 } 447 close(s); 448 reuseportsupported = rv; 449 reuseportsupported_set = JNI_TRUE; 450 return reuseportsupported; 451 } 452 453 #else /* __linux__ */ 454 455 /* Non Linux. Functionality is not supported. So, throw UnsupportedOpExc */ 456 457 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setReusePortOption 458 (JNIEnv *env, jclass UNUSED, jobject fileDesc, jboolean on) 459 { 460 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 461 "unsupported socket option"); 462 } 463 464 JNIEXPORT jobject JNICALL Java_sun_net_ExtendedOptionsImpl_getReusePortOption 465 (JNIEnv *env, jclass UNUSED, jobject fileDesc) 466 { 467 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 468 "unsupported socket option"); 469 return JNI_FALSE; 470 } 471 472 static jboolean reuseportSupported0() { 473 return JNI_FALSE; 474 } 475 #endif /* __linux__ */ 476 477 JNIEXPORT jboolean JNICALL Java_sun_net_ExtendedOptionsImpl_flowSupported 478 (JNIEnv *env, jclass UNUSED) 479 { 480 return flowSupported0(); 481 } 482 483 JNIEXPORT jboolean JNICALL Java_sun_net_ExtendedOptionsImpl_reuseportSupported 484 (JNIEnv *env, jclass UNUSED) 485 { 486 return reuseportSupported0(); 487 }