• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2010, The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "AsynchronousSocketCloseMonitor.h"
18 #include "JNIHelp.h"
19 #include "JniException.h"
20 #include "JniConstants.h"
21 #include "NetFd.h"
22 #include "NetworkUtilities.h"
23 #include "ScopedUtfChars.h"
24 #include "ScopedPrimitiveArray.h"
25 
26 #include "jni.h"
27 
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <linux/rtnetlink.h>
31 #include <net/if.h>
32 #include <linux/if_ether.h>
33 #include <linux/if_packet.h>
34 #include <arpa/inet.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <poll.h>
38 #include <netinet/ip.h>
39 #include <linux/udp.h>
40 
41 union sockunion {
42     sockaddr sa;
43     sockaddr_ll sll;
44 };
45 
46 /*
47  * Creates a socket suitable for raw socket operations.  The socket is
48  * bound to the interface specified by the supplied name.  The socket
49  * value is placed into the supplied FileDescriptor instance.
50  *
51  * TODO(chesnutt): consider breaking this into pieces: create a
52  * variety of constructors for different socket types, then a generic
53  * setBlocking() method followed by polymorphic bind().
54  */
RawSocket_create(JNIEnv * env,jclass,jobject fileDescriptor,jshort protocolType,jstring interfaceName)55 static void RawSocket_create(JNIEnv* env, jclass, jobject fileDescriptor,
56     jshort protocolType, jstring interfaceName)
57 {
58 
59   ScopedUtfChars ifname(env, interfaceName);
60   if (ifname.c_str() == NULL) {
61     return;
62   }
63 
64   sockunion su;
65   memset(&su, 0, sizeof(su));
66   su.sll.sll_family = PF_PACKET;
67   su.sll.sll_protocol = htons(protocolType);
68   su.sll.sll_ifindex = if_nametoindex(ifname.c_str());
69   int sock = socket(PF_PACKET, SOCK_DGRAM, htons(protocolType));
70 
71   if (sock == -1) {
72     ALOGE("Can't create socket %s", strerror(errno));
73     jniThrowSocketException(env, errno);
74     return;
75   }
76 
77   jniSetFileDescriptorOfFD(env, fileDescriptor, sock);
78   if (!setBlocking(sock, false)) {
79     ALOGE("Can't set non-blocking mode on socket %s", strerror(errno));
80     jniThrowSocketException(env, errno);
81     return;
82   }
83 
84   int err = bind(sock, &su.sa, sizeof(su));
85   if (err != 0) {
86     ALOGE("Socket bind error %s", strerror(errno));
87     jniThrowSocketException(env, errno);
88     return;
89   }
90 }
91 
92 /*
93  * Writes the L3 (IP) packet to the raw socket supplied in the
94  * FileDescriptor instance.
95  *
96  * Assumes that the caller has validated the offset & byteCount values.
97  */
RawSocket_sendPacket(JNIEnv * env,jclass,jobject fileDescriptor,jstring interfaceName,jshort protocolType,jbyteArray destMac,jbyteArray packet,jint offset,jint byteCount)98 static int RawSocket_sendPacket(JNIEnv* env, jclass, jobject fileDescriptor,
99     jstring interfaceName, jshort protocolType, jbyteArray destMac,
100     jbyteArray packet, jint offset, jint byteCount)
101 {
102   NetFd fd(env, fileDescriptor);
103 
104   if (fd.isClosed()) {
105     return 0;
106   }
107 
108   ScopedUtfChars ifname(env, interfaceName);
109   if (ifname.c_str() == NULL) {
110     return 0;
111   }
112 
113   ScopedByteArrayRO byteArray(env, packet);
114   if (byteArray.get() == NULL) {
115     return 0;
116   }
117 
118   ScopedByteArrayRO mac(env, destMac);
119   if (mac.get() == NULL) {
120     return 0;
121   }
122 
123   sockunion su;
124   memset(&su, 0, sizeof(su));
125   su.sll.sll_hatype = htons(1); // ARPHRD_ETHER
126   su.sll.sll_halen = mac.size();
127   memcpy(&su.sll.sll_addr, mac.get(), mac.size());
128   su.sll.sll_family = AF_PACKET;
129   su.sll.sll_protocol = htons(protocolType);
130   su.sll.sll_ifindex = if_nametoindex(ifname.c_str());
131 
132   int err;
133   {
134     int intFd = fd.get();
135     AsynchronousSocketCloseMonitor monitor(intFd);
136     err = NET_FAILURE_RETRY(fd, sendto(intFd, byteArray.get() + offset,
137         byteCount, 0, &su.sa, sizeof(su)));
138   }
139 
140   return err;
141 }
142 
143 /*
144  * Reads a network packet into the user-supplied buffer.  Return the
145  * length of the packet, or a 0 if there was a timeout or an
146  * unacceptable packet was acquired.
147  *
148  * Assumes that the caller has validated the offset & byteCount values.
149  */
RawSocket_recvPacket(JNIEnv * env,jclass,jobject fileDescriptor,jbyteArray packet,jint offset,jint byteCount,jint port,jint timeout_millis)150 static jint RawSocket_recvPacket(JNIEnv* env, jclass, jobject fileDescriptor,
151     jbyteArray packet, jint offset, jint byteCount, jint port,
152     jint timeout_millis)
153 {
154   NetFd fd(env, fileDescriptor);
155   if (fd.isClosed()) {
156     return 0;
157   }
158 
159   ScopedByteArrayRW body(env, packet);
160   jbyte* packetData = body.get();
161   if (packetData == NULL) {
162     return 0;
163   }
164 
165   packetData += offset;
166 
167   pollfd fds[1];
168   fds[0].fd = fd.get();
169   fds[0].events = POLLIN;
170   int retval = poll(fds, 1, timeout_millis);
171   if (retval <= 0) {
172     return 0;
173   }
174 
175   unsigned int size = 0;
176   {
177     int packetSize = byteCount;
178     int intFd = fd.get();
179     AsynchronousSocketCloseMonitor monitor(intFd);
180     size = NET_FAILURE_RETRY(fd, read(intFd, packetData, packetSize));
181   }
182 
183   if (env->ExceptionOccurred()) {
184     return 0;
185   }
186 
187   if (port != -1) {
188     // quick check for UDP type & UDP port
189     // the packet is an IP header, UDP header, and UDP payload
190     if ((size < (sizeof(struct iphdr) + sizeof(struct udphdr)))) {
191       return 0;  // runt packet
192     }
193 
194     u_int8_t ip_proto = ((iphdr *) packetData)->protocol;
195     if (ip_proto != IPPROTO_UDP) {
196       return 0;  // something other than UDP
197     }
198 
199     __be16 destPort = htons((reinterpret_cast<udphdr*>(packetData + sizeof(iphdr)))->dest);
200     if (destPort != port) {
201       return 0; // something other than requested port
202     }
203   }
204 
205   return size;
206 }
207 
208 static JNINativeMethod gRawMethods[] = {
209   NATIVE_METHOD(RawSocket, create, "(Ljava/io/FileDescriptor;SLjava/lang/String;)V"),
210   NATIVE_METHOD(RawSocket, sendPacket, "(Ljava/io/FileDescriptor;Ljava/lang/String;S[B[BII)I"),
211   NATIVE_METHOD(RawSocket, recvPacket, "(Ljava/io/FileDescriptor;[BIIII)I"),
212 };
213 
register_libcore_net_RawSocket(JNIEnv * env)214 void register_libcore_net_RawSocket(JNIEnv* env) {
215   jniRegisterNativeMethods(env, "libcore/net/RawSocket", gRawMethods, NELEM(gRawMethods));
216 }
217