| /* |
| * Copyright 2010, The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "AsynchronousSocketCloseMonitor.h" |
| #include "JNIHelp.h" |
| #include "JniException.h" |
| #include "JniConstants.h" |
| #include "NetFd.h" |
| #include "NetworkUtilities.h" |
| #include "ScopedUtfChars.h" |
| #include "ScopedPrimitiveArray.h" |
| |
| #include "jni.h" |
| |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <linux/rtnetlink.h> |
| #include <net/if.h> |
| #include <linux/if_ether.h> |
| #include <linux/if_packet.h> |
| #include <arpa/inet.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <poll.h> |
| #include <netinet/ip.h> |
| #include <linux/udp.h> |
| |
| union sockunion { |
| sockaddr sa; |
| sockaddr_ll sll; |
| }; |
| |
| /* |
| * Creates a socket suitable for raw socket operations. The socket is |
| * bound to the interface specified by the supplied name. The socket |
| * value is placed into the supplied FileDescriptor instance. |
| * |
| * TODO(chesnutt): consider breaking this into pieces: create a |
| * variety of constructors for different socket types, then a generic |
| * setBlocking() method followed by polymorphic bind(). |
| */ |
| static void RawSocket_create(JNIEnv* env, jclass, jobject fileDescriptor, |
| jshort protocolType, jstring interfaceName) |
| { |
| |
| ScopedUtfChars ifname(env, interfaceName); |
| if (ifname.c_str() == NULL) { |
| return; |
| } |
| |
| sockunion su; |
| memset(&su, 0, sizeof(su)); |
| su.sll.sll_family = PF_PACKET; |
| su.sll.sll_protocol = htons(protocolType); |
| su.sll.sll_ifindex = if_nametoindex(ifname.c_str()); |
| int sock = socket(PF_PACKET, SOCK_DGRAM, htons(protocolType)); |
| |
| if (sock == -1) { |
| ALOGE("Can't create socket %s", strerror(errno)); |
| jniThrowSocketException(env, errno); |
| return; |
| } |
| |
| jniSetFileDescriptorOfFD(env, fileDescriptor, sock); |
| if (!setBlocking(sock, false)) { |
| ALOGE("Can't set non-blocking mode on socket %s", strerror(errno)); |
| jniThrowSocketException(env, errno); |
| return; |
| } |
| |
| int err = bind(sock, &su.sa, sizeof(su)); |
| if (err != 0) { |
| ALOGE("Socket bind error %s", strerror(errno)); |
| jniThrowSocketException(env, errno); |
| return; |
| } |
| } |
| |
| /* |
| * Writes the L3 (IP) packet to the raw socket supplied in the |
| * FileDescriptor instance. |
| * |
| * Assumes that the caller has validated the offset & byteCount values. |
| */ |
| static int RawSocket_sendPacket(JNIEnv* env, jclass, jobject fileDescriptor, |
| jstring interfaceName, jshort protocolType, jbyteArray destMac, |
| jbyteArray packet, jint offset, jint byteCount) |
| { |
| NetFd fd(env, fileDescriptor); |
| |
| if (fd.isClosed()) { |
| return 0; |
| } |
| |
| ScopedUtfChars ifname(env, interfaceName); |
| if (ifname.c_str() == NULL) { |
| return 0; |
| } |
| |
| ScopedByteArrayRO byteArray(env, packet); |
| if (byteArray.get() == NULL) { |
| return 0; |
| } |
| |
| ScopedByteArrayRO mac(env, destMac); |
| if (mac.get() == NULL) { |
| return 0; |
| } |
| |
| sockunion su; |
| memset(&su, 0, sizeof(su)); |
| su.sll.sll_hatype = htons(1); // ARPHRD_ETHER |
| su.sll.sll_halen = mac.size(); |
| memcpy(&su.sll.sll_addr, mac.get(), mac.size()); |
| su.sll.sll_family = AF_PACKET; |
| su.sll.sll_protocol = htons(protocolType); |
| su.sll.sll_ifindex = if_nametoindex(ifname.c_str()); |
| |
| int err; |
| { |
| int intFd = fd.get(); |
| AsynchronousSocketCloseMonitor monitor(intFd); |
| err = NET_FAILURE_RETRY(fd, sendto(intFd, byteArray.get() + offset, |
| byteCount, 0, &su.sa, sizeof(su))); |
| } |
| |
| return err; |
| } |
| |
| /* |
| * Reads a network packet into the user-supplied buffer. Return the |
| * length of the packet, or a 0 if there was a timeout or an |
| * unacceptable packet was acquired. |
| * |
| * Assumes that the caller has validated the offset & byteCount values. |
| */ |
| static jint RawSocket_recvPacket(JNIEnv* env, jclass, jobject fileDescriptor, |
| jbyteArray packet, jint offset, jint byteCount, jint port, |
| jint timeout_millis) |
| { |
| NetFd fd(env, fileDescriptor); |
| if (fd.isClosed()) { |
| return 0; |
| } |
| |
| ScopedByteArrayRW body(env, packet); |
| jbyte* packetData = body.get(); |
| if (packetData == NULL) { |
| return 0; |
| } |
| |
| packetData += offset; |
| |
| pollfd fds[1]; |
| fds[0].fd = fd.get(); |
| fds[0].events = POLLIN; |
| int retval = poll(fds, 1, timeout_millis); |
| if (retval <= 0) { |
| return 0; |
| } |
| |
| unsigned int size = 0; |
| { |
| int packetSize = byteCount; |
| int intFd = fd.get(); |
| AsynchronousSocketCloseMonitor monitor(intFd); |
| size = NET_FAILURE_RETRY(fd, read(intFd, packetData, packetSize)); |
| } |
| |
| if (env->ExceptionOccurred()) { |
| return 0; |
| } |
| |
| if (port != -1) { |
| // quick check for UDP type & UDP port |
| // the packet is an IP header, UDP header, and UDP payload |
| if ((size < (sizeof(struct iphdr) + sizeof(struct udphdr)))) { |
| return 0; // runt packet |
| } |
| |
| u_int8_t ip_proto = ((iphdr *) packetData)->protocol; |
| if (ip_proto != IPPROTO_UDP) { |
| return 0; // something other than UDP |
| } |
| |
| __be16 destPort = htons((reinterpret_cast<udphdr*>(packetData + sizeof(iphdr)))->dest); |
| if (destPort != port) { |
| return 0; // something other than requested port |
| } |
| } |
| |
| return size; |
| } |
| |
| static JNINativeMethod gRawMethods[] = { |
| NATIVE_METHOD(RawSocket, create, "(Ljava/io/FileDescriptor;SLjava/lang/String;)V"), |
| NATIVE_METHOD(RawSocket, sendPacket, "(Ljava/io/FileDescriptor;Ljava/lang/String;S[B[BII)I"), |
| NATIVE_METHOD(RawSocket, recvPacket, "(Ljava/io/FileDescriptor;[BIIII)I"), |
| }; |
| |
| void register_libcore_net_RawSocket(JNIEnv* env) { |
| jniRegisterNativeMethods(env, "libcore/net/RawSocket", gRawMethods, NELEM(gRawMethods)); |
| } |